diff --git a/.github/workflows/end-to-end-test.yml b/.github/workflows/end-to-end-test.yml index a74408be..0fa65d6c 100644 --- a/.github/workflows/end-to-end-test.yml +++ b/.github/workflows/end-to-end-test.yml @@ -81,7 +81,7 @@ jobs: - name: Install go uses: actions/setup-go@v3 with: - go-version: 1.20.x + go-version: 1.21.x - name: Checkout zot repo uses: actions/checkout@v3 diff --git a/src/__tests__/TagPage/VulnerabilitiesDetails.test.js b/src/__tests__/TagPage/VulnerabilitiesDetails.test.js index 5c980ca5..7b13ff2d 100644 --- a/src/__tests__/TagPage/VulnerabilitiesDetails.test.js +++ b/src/__tests__/TagPage/VulnerabilitiesDetails.test.js @@ -510,23 +510,15 @@ afterEach(() => { describe('Vulnerabilties page', () => { it('renders the vulnerabilities if there are any', async () => { - let getCall = jest.spyOn(api, 'get').mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }); - for (let i=0; i<21; i++) { - getCall.mockResolvedValueOnce({ status: 200, data: { data: mockCVEFixed.pageNotFixed } }); - } - getCall.mockResolvedValue({ status: 200, data: { data: mockCVEList } }); + jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockCVEList } }); render(); await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1)); await waitFor(() => expect(screen.getAllByText('Total 5')).toHaveLength(1)); - await waitFor(() => expect(screen.getAllByText(/Fixed in/)).toHaveLength(20)); + await waitFor(() => expect(screen.getAllByText(/CVE/)).toHaveLength(20)); }); it('sends filtered query if user types in the search bar', async () => { - let getCall = jest.spyOn(api, 'get').mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }); - for (let i=0; i<21; i++) { - getCall.mockResolvedValueOnce({ status: 200, data: { data: mockCVEFixed.pageNotFixed } }); - } - getCall.mockResolvedValue({ status: 200, data: { data: mockCVEList } }); + jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockCVEList } }); render(); const cveSearchInput = screen.getByPlaceholderText(/search/i); jest.spyOn(api, 'get').mockRejectedValueOnce({ status: 200, data: { data: mockCVEListFiltered } }); @@ -536,11 +528,7 @@ describe('Vulnerabilties page', () => { }); it('should have a collapsable search bar', async () => { - let getCall = jest.spyOn(api, 'get').mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }) - for (let i=0; i<21; i++) { - getCall.mockResolvedValueOnce({ status: 200, data: { data: mockCVEFixed.pageNotFixed } }); - } - getCall.mockResolvedValue({ status: 200, data: { data: mockCVEList } }); + jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockCVEList } }); render(); const cveSearchInput = screen.getByPlaceholderText(/search/i); const expandSearch = cveSearchInput.parentElement.parentElement.parentElement.parentElement.childNodes[0]; @@ -566,6 +554,8 @@ describe('Vulnerabilties page', () => { jest.spyOn(api, 'get').mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }) .mockResolvedValue({ status: 200, data: { data: mockCVEFixed.pageNotFixed } }); render(); + const expandListBtn = await screen.findAllByTestId('ViewAgendaIcon'); + fireEvent.click(expandListBtn[0]); await waitFor(() => expect(screen.getAllByText(/Description/)).toHaveLength(20)); await waitFor(() => expect(screen.getAllByText(/CPAN 2.28 allows Signature Verification Bypass./i)).toHaveLength(1) @@ -587,12 +577,13 @@ describe('Vulnerabilties page', () => { .mockResolvedValueOnce({ status: 200, data: { data: mockCVEFixed.pageTwo } }); render(); await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1)); + const expandListBtn = await screen.findAllByTestId('KeyboardArrowRightIcon'); + fireEvent.click(expandListBtn[1]); await waitFor(() => expect(screen.getByText('1.0.16')).toBeInTheDocument()); - await waitFor(() => expect(screen.getAllByText(/load more/i).length).toBeGreaterThan(0)); - const nrLoadButtons = screen.getAllByText(/load more/i).length - const loadMoreBtn = screen.getAllByText(/load more/i)[0]; + await waitFor(() => expect(screen.getAllByText(/Load more/).length).toBe(1)); + const loadMoreBtn = screen.getAllByText(/Load more/)[0]; await fireEvent.click(loadMoreBtn); - await waitFor(() => expect(screen.getAllByText(/load more/i).length).toBe(nrLoadButtons-1)); + await waitFor(() => expect(loadMoreBtn).not.toBeInTheDocument()); expect(await screen.findByText('latest')).toBeInTheDocument(); }); @@ -600,11 +591,7 @@ describe('Vulnerabilties page', () => { const xlsxMock = jest.createMockFromModule('xlsx'); xlsxMock.writeFile = jest.fn(); - let getCall = jest.spyOn(api, 'get').mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }); - for (let i=0; i<21; i++) { - getCall.mockResolvedValueOnce({ status: 200, data: { data: mockCVEFixed.pageNotFixed } }); - } - getCall.mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }); + jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockCVEList } }); render(); await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1)); const downloadBtn = await screen.findAllByTestId('DownloadIcon'); @@ -631,13 +618,12 @@ describe('Vulnerabilties page', () => { getCall.mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }); render(); await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1)); + const expandListBtn = await screen.findAllByTestId('ViewAgendaIcon'); + fireEvent.click(expandListBtn[0]); await waitFor(() => expect(screen.getAllByText('Fixed in')).toHaveLength(20)); const collapseListBtn = await screen.findAllByTestId('ViewHeadlineIcon'); fireEvent.click(collapseListBtn[0]); expect(await screen.findByText('Fixed in')).not.toBeVisible(); - const expandListBtn = await screen.findAllByTestId('ViewAgendaIcon'); - fireEvent.click(expandListBtn[0]); - await waitFor(() => expect(screen.getAllByText('Fixed in')).toHaveLength(20)); }); it('should handle fixed CVE query errors', async () => { @@ -648,6 +634,8 @@ describe('Vulnerabilties page', () => { render(); await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1)); const error = jest.spyOn(console, 'error').mockImplementation(() => {}); + const expandListBtn = await screen.findAllByTestId('ViewAgendaIcon'); + fireEvent.click(expandListBtn[0]); await waitFor(() => expect(screen.getAllByText(/not fixed/i).length).toBeGreaterThan(0)); await waitFor(() => expect(error).toBeCalled()); }); diff --git a/src/components/Shared/VulnerabilityCard.jsx b/src/components/Shared/VulnerabilityCard.jsx index c201c789..423eed22 100644 --- a/src/components/Shared/VulnerabilityCard.jsx +++ b/src/components/Shared/VulnerabilityCard.jsx @@ -123,9 +123,10 @@ function VulnerabilitiyCard(props) { // pagination props const [pageNumber, setPageNumber] = useState(1); const [isEndOfList, setIsEndOfList] = useState(false); + const [loadMoreInfo, setLoadMoreInfo] = useState(false); const getPaginatedResults = () => { - if (isEndOfList) { + if (!openCVE || (!loadMoreInfo && !isEmpty(fixedInfo)) || isEndOfList) { return; } setLoadingFixed(true); @@ -148,11 +149,13 @@ function VulnerabilitiyCard(props) { ); } setLoadingFixed(false); + setLoadMoreInfo(false); }) .catch((e) => { console.error(e); setIsEndOfList(true); setLoadingFixed(false); + setLoadMoreInfo(false); }); }; @@ -161,7 +164,7 @@ function VulnerabilitiyCard(props) { return () => { abortController.abort(); }; - }, [pageNumber]); + }, [openCVE, pageNumber, loadMoreInfo]); useEffect(() => { setOpenCVE(expand); @@ -169,6 +172,7 @@ function VulnerabilitiyCard(props) { const loadMore = () => { if (loadingFixed || isEndOfList) return; + setLoadMoreInfo(true); setPageNumber((pageNumber) => pageNumber + 1); }; diff --git a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx index 38c1a614..76fb17cd 100644 --- a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx +++ b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx @@ -165,7 +165,7 @@ function VulnerabilitiesDetails(props) { const [anchorExport, setAnchorExport] = useState(null); const openExport = Boolean(anchorExport); - const [selectedViewMore, setSelectedViewMore] = useState(true); + const [selectedViewMore, setSelectedViewMore] = useState(false); const getCVERequestName = () => { return digest !== '' ? `${name}@${digest}` : `${name}:${tag}`;