Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 11 commits into from
Nov 29, 2023
Merged
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 @@ -143,7 +143,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
Loading