diff --git a/src/__tests__/TagPage/VulnerabilitiesDetails.test.js b/src/__tests__/TagPage/VulnerabilitiesDetails.test.js index 13d3cdff..c8dcadc6 100644 --- a/src/__tests__/TagPage/VulnerabilitiesDetails.test.js +++ b/src/__tests__/TagPage/VulnerabilitiesDetails.test.js @@ -586,6 +586,22 @@ describe('Vulnerabilties page', () => { expect(await screen.findByTestId('export-excel-menuItem')).not.toBeInTheDocument(); }); + it('should expand/collapse the list of CVEs', async () => { + jest + .spyOn(api, 'get') + .mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }) + .mockResolvedValueOnce({ status: 200, data: { data: mockCVEList } }); + render(); + await waitFor(() => expect(screen.getAllByText('Vulnerabilities')).toHaveLength(1)); + 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 () => { jest .spyOn(api, 'get') diff --git a/src/components/Shared/VulnerabilityCard.jsx b/src/components/Shared/VulnerabilityCard.jsx index 0ee3b7cd..ea3a0a69 100644 --- a/src/components/Shared/VulnerabilityCard.jsx +++ b/src/components/Shared/VulnerabilityCard.jsx @@ -66,13 +66,18 @@ const useStyles = makeStyles((theme) => ({ cursor: 'pointer', textAlign: 'center' }, + dropdownCVE: { + color: '#1479FF', + cursor: 'pointer' + }, vulnerabilityCardDivider: { margin: '1rem 0' } })); function VulnerabilitiyCard(props) { const classes = useStyles(); - const { cve, name, platform } = props; + const { cve, name, platform, expand } = props; + const [openCVE, setOpenCVE] = useState(expand); const [openDesc, setOpenDesc] = useState(false); const [openFixed, setOpenFixed] = useState(false); const [loadingFixed, setLoadingFixed] = useState(true); @@ -122,6 +127,10 @@ function VulnerabilitiyCard(props) { }; }, [openFixed, pageNumber]); + useEffect(() => { + setOpenCVE(expand); + }, [expand]); + const loadMore = () => { if (loadingFixed || isEndOfList) return; setPageNumber((pageNumber) => pageNumber + 1); @@ -166,49 +175,56 @@ function VulnerabilitiyCard(props) { + {!openCVE ? ( + setOpenCVE(!openCVE)} /> + ) : ( + setOpenCVE(!openCVE)} /> + )} {cve.id} - - {cve.title} - - - setOpenFixed(!openFixed)}> - {!openFixed ? ( - - ) : ( - - )} - Fixed in - - - - {loadingFixed ? ( - 'Loading...' + + + {cve.title} + + + setOpenFixed(!openFixed)}> + {!openFixed ? ( + ) : ( - - {renderFixedVer()} - {renderLoadMore()} - + )} - - - setOpenDesc(!openDesc)}> - {!openDesc ? ( - - ) : ( - - )} - Description - - - - - {cve.description} - - + Fixed in + + + + {loadingFixed ? ( + 'Loading...' + ) : ( + + {renderFixedVer()} + {renderLoadMore()} + + )} + + + setOpenDesc(!openDesc)}> + {!openDesc ? ( + + ) : ( + + )} + Description + + + + + {cve.description} + + + diff --git a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx index 1872f6a0..9b7ba323 100644 --- a/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx +++ b/src/components/Tag/Tabs/VulnerabilitiesDetails.jsx @@ -9,6 +9,7 @@ import { Stack, Typography, InputBase, + ToggleButton, Menu, MenuItem, Divider, @@ -26,6 +27,8 @@ import DownloadIcon from '@mui/icons-material/Download'; import * as XLSX from 'xlsx'; import exportFromJSON from 'export-from-json'; +import ViewHeadlineIcon from '@mui/icons-material/ViewHeadline'; +import ViewAgendaIcon from '@mui/icons-material/ViewAgenda'; import VulnerabilitiyCard from '../../Shared/VulnerabilityCard'; @@ -71,6 +74,17 @@ const useStyles = makeStyles((theme) => ({ border: '0.063rem solid #E7E7E7', borderRadius: '0.625rem' }, + view: { + alignContent: 'right', + variant: 'outlined' + }, + viewModes: { + position: 'relative', + maxWidth: '100%', + flexDirection: 'row', + alignItems: 'right', + justifyContent: 'right' + }, searchIcon: { color: '#52637A', paddingRight: '3%' @@ -87,9 +101,6 @@ const useStyles = makeStyles((theme) => ({ opacity: '1' } }, - export: { - alignContent: 'right' - }, popper: { width: '100%', overflow: 'hidden', @@ -117,6 +128,8 @@ function VulnerabilitiesDetails(props) { const [anchorExport, setAnchorExport] = useState(null); const openExport = Boolean(anchorExport); + const [selectedViewMore, setSelectedViewMore] = useState(true); + const getCVERequestName = () => { return digest !== '' ? `${name}@${digest}` : `${name}:${tag}`; }; @@ -263,7 +276,7 @@ function VulnerabilitiesDetails(props) { const renderCVEs = () => { return !isEmpty(cveData) ? ( cveData.map((cve, index) => { - return ; + return ; }) ) : (
{!isLoading && No Vulnerabilities }
@@ -286,14 +299,36 @@ function VulnerabilitiesDetails(props) { Vulnerabilities - - - - } - /> + + + + + } + /> + setSelectedViewMore(false)} + > + + + setSelectedViewMore(true)} + > + + + { await page.goto(`${hosts.ui}/image/${tagWithVulnerabilities.title}/tag/${tagWithVulnerabilities.tag}`); await page.getByRole('tab', { name: 'Vulnerabilities' }).click(); await expect(page.getByTestId('vulnerability-container').locator('div').nth(1)).toBeVisible({ timeout: 100000 }); + await expect(page.getByText('CVE-').nth(1)).toBeVisible({ timeout: 150000 }); await expect(await page.getByText('CVE-').count()).toBeGreaterThan(0); await expect(await page.getByText('CVE-').count()).toBeLessThanOrEqual(pageSizes.EXPLORE); });