Skip to content

Commit

Permalink
feat(RHIF-283): extend CVE systems filter with edge (#2002)
Browse files Browse the repository at this point in the history
* chore(VulnerabilityRoutes): introduce loading state

* feat(RHIF-283): extend CVE systems filter with edge

* fix(RHIF-283): change conventional and immutable filter titles

* chore: adjust api request filter key according to flag status

* chore(tests): fix tests

* chore(tests): add test coverage for flag disabled state

* chore(tests): disaable mocked feature flag in ReportPage tests

* WIP: chore(tests): reportsPage changes with tests

* chore(rhif-283): show extended filter only when there is edge

* chore(tests): ReportsPage has tests finished.

* chore(tests): improve tests for CVEsTableToolbar
  • Loading branch information
mkholjuraev authored Nov 29, 2023
1 parent 8f2390b commit acde36d
Show file tree
Hide file tree
Showing 29 changed files with 362 additions and 275 deletions.
3 changes: 2 additions & 1 deletion config/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ jest.mock('@redhat-cloud-services/frontend-components-pdf-generator', () => {
PanelItem: () => (<div />),
InsightsLabel: () => (<div />),
Table: () => (<div />),
Paragraph: () => (<div />)
Paragraph: () => (<div />),
DownloadButton: jest.fn((props) => <div {...props} aria-label='FEC download button'/>)
};
});

Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@redhat-cloud-services/frontend-components-translations": "^3.2.4",
"@redhat-cloud-services/frontend-components-utilities": "^3.7.6",
"@redhat-cloud-services/host-inventory-client": "^1.0.116",
"@redhat-cloud-services/vulnerabilities-client": "^1.2.10",
"@redhat-cloud-services/vulnerabilities-client": "^1.2.12",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^12.8.3",
Expand Down Expand Up @@ -144,7 +144,7 @@
"build": "webpack --config config/prod.webpack.config.js",
"test:ct": "BABEL_ENV=componentTest cypress run --component",
"test:openct": "cypress open --component",
"test:jest": "TZ=UTC jest --silent --no-cache --passWithNoTests --env=jsdom",
"test:jest": "TZ=UTC jest --no-cache --passWithNoTests --env=jsdom",
"test:jest:cache": "TZ=UTC jest --silent --passWithNoTests --env=jsdom",
"test": "npm run test:jest",
"test:jest:watch": "TZ=UTC jest --watch --passWithNoTests --no-cache",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { conditionalFilterType } from '@redhat-cloud-services/frontend-components/ConditionalFilter';
import { intl } from '../../../../Utilities/IntlProvider';
import messages from '../../../../Messages';
import { AFFECTING_FILTER_OPTIONS } from '../../../../Helpers/constants';
import { getAffectingFilterOptions } from '../../../../Helpers/constants';

const affectingFilter = (apply, currentFilter = {}) => {
const affectingFilter = (apply, currentFilter = {}, shouldUseHybridSystemFilter) => {
let { affecting: currentValue } = currentFilter;

const filterByAffecting = (values) => {
Expand All @@ -13,6 +13,7 @@ const affectingFilter = (apply, currentFilter = {}) => {
});
};

const filterItems = getAffectingFilterOptions(shouldUseHybridSystemFilter);
return {
label: intl.formatMessage(messages.filterSystemsExposed),
type: conditionalFilterType.checkbox,
Expand All @@ -21,7 +22,7 @@ const affectingFilter = (apply, currentFilter = {}) => {
onChange: (event, value) => {
filterByAffecting(value);
},
items: AFFECTING_FILTER_OPTIONS.map(({ label, value }) => ({ label, value })),
items: filterItems,
value: currentValue?.split(',')
}
};
Expand Down
26 changes: 18 additions & 8 deletions src/Components/SmartComponents/CVEs/CVEs.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ import {
} from '@redhat-cloud-services/frontend-components-notifications/redux';
import ErrorHandler from '../../PresentationalComponents/ErrorHandler/ErrorHandler';
import { Spinner } from '@redhat-cloud-services/frontend-components/Spinner';
import { useColumnManagement, useRbac } from '../../../Helpers/Hooks';
import { useColumnManagement, useHybridSystemFilterFlag, useRbac } from '../../../Helpers/Hooks';
import { NotAuthorized } from '../../PresentationalComponents/EmptyStates/EmptyStates';
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
import { getCveDefaultFilters } from './CVEsAssets';

export const CVETableContext = React.createContext({});

Expand Down Expand Up @@ -67,26 +68,27 @@ export const CVEs = ({ rbac }) => {
= useColumnManagement(columns, newColumns => dispatch(changeColumnsCveList(newColumns)));

const cves = useMemo(() => createCveListByAccount(cveList, columns, parameters), [cveList, columns, parameters]);
const [urlParameters, setUrlParam] = useUrlParams(['show_irrelevant', ...CVES_ALLOWED_PARAMS]);
const shouldUseHybridSystemFilter = useHybridSystemFilterFlag();
const [urlParameters, setUrlParam] = useUrlParams(['show_irrelevant', ...CVES_ALLOWED_PARAMS], shouldUseHybridSystemFilter);

const apply = (filterParams = {}) => {
const params = constructFilterParameters(filterParams);
dispatch(changeCveListParameters(params));
};

useEffect(() => {
apply(urlParameters);
}, []);
apply({ ...getCveDefaultFilters(shouldUseHybridSystemFilter), ...urlParameters });
}, [shouldUseHybridSystemFilter]);

useEffect(() => {
if (isFirstLoad) {
setFirstLoad(false);
}
else {
dispatch(fetchCveListByAccount(parameters));
dispatch(fetchCveListByAccount(parameters, shouldUseHybridSystemFilter));
setUrlParam({ ...parameters });
}
}, [parameters, isFirstLoad]);
}, [parameters, isFirstLoad, shouldUseHybridSystemFilter]);

useEffect(() => {
return () => {
Expand All @@ -100,8 +102,15 @@ export const CVEs = ({ rbac }) => {
};

const downloadReport = format => {
DownloadReport.exec(fetchCveListByAccount, parameters, format, 'cves', notification => dispatch(
addNotification(notification)), () => dispatch(clearNotifications()));
DownloadReport.exec(
fetchCveListByAccount,
parameters,
format,
'cves',
notification => dispatch(addNotification(notification)),
() => dispatch(clearNotifications()),
shouldUseHybridSystemFilter
);
};

const showBusinessRiskModal = (cvesList, goToFirstPage) => {
Expand Down Expand Up @@ -186,6 +195,7 @@ export const CVEs = ({ rbac }) => {
</Alert>
}
<CVEsTableToolbar
aria-label="CVEs table toolbar"
canEditStatusOrBusinessRisk={canEditStatusOrBusinessRisk}
canExport={canExport}
canToggleCvesWithoutErrata={canToggleCvesWithoutErrata}
Expand Down
63 changes: 61 additions & 2 deletions src/Components/SmartComponents/CVEs/CVEs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ import { useSelector } from 'react-redux';
import DownloadReport from '../../../Helpers/DownloadReport';
import { act } from "react-dom/test-utils";
import { CVES_DEFAULT_FILTERS } from '../../../Helpers/constants';
import { ComponentWithContext } from '../../../Utilities/TestingUtilities';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useHybridSystemFilterFlag } from '../../../Helpers/Hooks';

import '@testing-library/jest-dom';

jest.mock('../../../Helpers/Hooks', () => ({
...jest.requireActual('../../../Helpers/Hooks'),
useRbac: () => [[true, true, true, true], false]
useRbac: () => [[true, true, true, true], false],
useHybridSystemFilterFlag: jest.fn(() => false)
}));

jest.mock('../../../Store/Actions/Actions', () => ({
Expand Down Expand Up @@ -245,4 +252,56 @@ describe('CVEs', () => {
expect(action[1].payload).toEqual({});
expect(action).toHaveLength(2);
});
})

});

describe('CVEs RTL', () => {
it('Should show extended Systems filter when edge parity feature is disabled', async () => {
render(<ComponentWithContext renderOptions={{ store }}>
<CVEs />
</ComponentWithContext>);

userEvent.click(screen.getByRole('button', {
name: /conditional filter/i
}));

userEvent.click(screen.getByText(
'Systems',
{ selector: '.pf-c-dropdown__menu-item' }
));

userEvent.click(screen.getByText(
'Filter by systems',
{ selector: '.pf-c-select__toggle-text' }
));

expect(screen.getByText('1 or more')).toBeVisible();
expect(screen.getByText('None')).toBeVisible();
});

it('Should show extended Systems filter when edge parity feature is enabled', async () => {
useHybridSystemFilterFlag.mockReturnValue(true);
render(<ComponentWithContext renderOptions={{ store }}>
<CVEs />
</ComponentWithContext>);

userEvent.click(screen.getByRole('button', {
name: /conditional filter/i
}));

userEvent.click(screen.getByText(
'Systems',
{ selector: '.pf-c-dropdown__menu-item' }
));

userEvent.click(screen.getByText(
'Filter by systems',
{ selector: '.pf-c-select__toggle-text' }
));

expect(screen.getByText('1 or more Conventional (OSTree)')).toBeVisible();
expect(screen.getByText('1 or more Immutable (RPM-DNF)')).toBeVisible();
expect(screen.getByText('None')).toBeVisible();
});
});

8 changes: 4 additions & 4 deletions src/Components/SmartComponents/CVEs/CVEsAssets.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import { classNames, expandable, sortable, nowrap, wrappable } from '@patternfly
import messages from '../../../Messages';
import { intl } from '../../../Utilities/IntlProvider';

export const CVES_DEFAULT_FILTERS = {
affecting: 'true'
};

export const VULNERABILITIES_HEADER = [
{
title: intl.formatMessage(messages.cveId),
Expand Down Expand Up @@ -56,3 +52,7 @@ export const VULNERABILITIES_HEADER = [
isShownByDefault: true
}
];

export const getCveDefaultFilters = (shouldUseHybridSystemFilter) => {
return { affecting: shouldUseHybridSystemFilter ? 'rpmdnf,edge' : 'true' };
};
15 changes: 10 additions & 5 deletions src/Components/SmartComponents/CVEs/CVEsTableToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ import {
isFilterInDefaultState
} from '../../../Helpers/TableToolbarHelper';
import {
CVES_DEFAULT_FILTERS,
CVES_FILTER_PARAMS,
DEFAULT_PAGE_SIZE,
ONLY_NON_VULNERABLE_SYSTEMS,
RULE_PRESENCE_OPTIONS
} from '../../../Helpers/constants';
import { fetchCvesIds } from '../../../Store/Actions/Actions';
import { setCvesWithoutErrata } from '../../../Helpers/APIHelper';
import { getCveDefaultFilters } from './CVEsAssets';
import { useHybridSystemFilterFlag } from '../../../Helpers/Hooks';

const CVEsTableToolbarWithContext = ({ context, canEditStatusOrBusinessRisk, canExport, canToggleCvesWithoutErrata, intl }) => {
const [exportPDF, setExportPDF] = useState(false);
Expand All @@ -46,6 +47,7 @@ const CVEsTableToolbarWithContext = ({ context, canEditStatusOrBusinessRisk, can
const selectedCvesCount = selectedCves && selectedCves.length;

const [showCvesWithoutErrata, setShowCvesWithoutErrata] = useState(null);
const shouldUseHybridSystemFilter = useHybridSystemFilterFlag();

useEffect(() => {
if (!isLoading) {
Expand All @@ -67,7 +69,10 @@ const CVEsTableToolbarWithContext = ({ context, canEditStatusOrBusinessRisk, can
}
}, [cves?.meta?.cves_without_errata, isLoading]);

const defaultFilters = { ...CVES_DEFAULT_FILTERS, ...cves?.meta?.cves_without_errata ? { advisory_available: 'true' } : {} };
const defaultFilters = {
...getCveDefaultFilters(shouldUseHybridSystemFilter),
...cves?.meta?.cves_without_errata ? { advisory_available: 'true' } : {}
};

const selectOptions = selectAllCheckbox({
selectedItems: selectedCves,
Expand Down Expand Up @@ -169,14 +174,14 @@ const CVEsTableToolbarWithContext = ({ context, canEditStatusOrBusinessRisk, can
impactFilter(methods.apply, params),
useCvssBaseScoreFilter(methods.apply, params),
businessRiskFilter(methods.apply, params),
affectingFilter(methods.apply, params),
affectingFilter(methods.apply, params, shouldUseHybridSystemFilter),
publishDateFilter(methods.apply, params),
statusFilter(methods.apply, params),
...showCvesWithoutErrata ? [advisoryAvailabilityFilter(methods.apply, params)] : []
]
}}
activeFiltersConfig={{
filters: buildActiveFilters(params),
filters: buildActiveFilters(params, [], shouldUseHybridSystemFilter),
onDelete: (_, chips, reset) => removeFilters(chips, methods.apply, reset, defaultFilters),
deleteTitle: intl.formatMessage(messages.resetFilters),
showDeleteButton: !isFilterInDefaultState(params, defaultFilters, CVES_FILTER_PARAMS)
Expand All @@ -192,7 +197,7 @@ const CVEsTableToolbarWithContext = ({ context, canEditStatusOrBusinessRisk, can
<DownloadCVEsReport
showButton={false}
params={params}
filters={buildActiveFilters(params)}
filters={buildActiveFilters(params, [], shouldUseHybridSystemFilter)}
onSuccess={() => setExportPDF(false)}
/>
}
Expand Down
34 changes: 34 additions & 0 deletions src/Components/SmartComponents/CVEs/CVEsTableToolbar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
handleSetPageSize,
removeFilters
} from '../../../Helpers/TableToolbarHelper';
import { useHybridSystemFilterFlag } from '../../../Helpers/Hooks';
import { getAffectingFilterOptions } from '../../../Helpers/constants';

jest.mock('../../../Helpers/TableToolbarHelper', () => ({
...(jest.requireActual('../../../Helpers/TableToolbarHelper')),
Expand All @@ -23,6 +25,11 @@ jest.mock('../../../Store/Actions/Actions', () => ({
}) }
));

jest.mock('../../../Helpers/Hooks', () => ({
...jest.requireActual('../../../Helpers/Hooks'),
useHybridSystemFilterFlag: jest.fn(() => false)
}));

const mockContext = {
cves: {
isLoading: false,
Expand Down Expand Up @@ -221,5 +228,32 @@ describe('CVEsTableToolbar', () => {
onDelete(null, 'testitem');
expect(removeFilters).toHaveBeenCalledWith('testitem', expect.any(Function), undefined, { "affecting": "true" });
});
it('Should hide edge related filters when edge flag is off', () => {
const testContext = { ...mockContext, selectedCves: ['CVE-2019-6454'] };
const wrapper = mountWithIntl(
<CVETableContext.Provider value={testContext}>
<CVEsTableToolbar {...props} />
</CVETableContext.Provider>
);
const { filterConfig: { items } } = wrapper.find('PrimaryToolbar').props();
const { filterValues} = items.find(item => item.label === 'systems');

expect(filterValues.items).toEqual(getAffectingFilterOptions(false));
});

it('Should show edge related filters when edge flag is on', () => {
useHybridSystemFilterFlag.mockReturnValue(true);
const testContext = { ...mockContext, selectedCves: ['CVE-2019-6454'] };
const wrapper = mountWithIntl(
<CVETableContext.Provider value={testContext}>
<CVEsTableToolbar {...props} />
</CVETableContext.Provider>
);
const { filterConfig: { items } } = wrapper.find('PrimaryToolbar').props();
const { filterValues} = items.find(item => item.label === 'systems');

expect(filterValues.items).toEqual(getAffectingFilterOptions(true));
});
});

});
Loading

0 comments on commit acde36d

Please sign in to comment.