mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[CloudSecurity] Converting Findings DistributionBar FTR into integration test (#186938)
## Summary It closes #176700 This PR converts the DistributionBar FTR test on the Findings page into an integration test using MSW. It also closes #176700as it was once triggering an error in the past Also, it adds the following changes: - Added a `generateMultipleCspFindings` helper to help with the writing of future tests and generating batch data. - Removed DistributionBar FTR test - Removed the extra layer of sub-components on the DistributionBar component to be simpler and added an aria-label on the distribution bar buttons. ## Screenshots  
This commit is contained in:
parent
5e353a3a00
commit
7ae1f7a7df
5 changed files with 160 additions and 67 deletions
|
@ -137,7 +137,7 @@ export const useCloudPostureDataTable = ({
|
|||
return {
|
||||
setUrlQuery,
|
||||
sort: urlQuery.sort,
|
||||
filters: urlQuery.filters,
|
||||
filters: urlQuery.filters || [],
|
||||
query: baseEsQuery.query
|
||||
? baseEsQuery.query
|
||||
: {
|
||||
|
|
|
@ -11,6 +11,15 @@ import { isArray } from 'lodash';
|
|||
import { http, HttpResponse } from 'msw';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
||||
export const generateMultipleCspFindings = (
|
||||
option: { count: number; failedCount?: number } = { count: 1, failedCount: 0 }
|
||||
) => {
|
||||
const failedCount = option.failedCount || 0;
|
||||
return Array.from({ length: option?.count }, (_, i) => {
|
||||
return generateCspFinding(i.toString(), i < failedCount ? 'failed' : 'passed');
|
||||
});
|
||||
};
|
||||
|
||||
export const generateCspFinding = (
|
||||
id: string,
|
||||
evaluation: 'failed' | 'passed' = 'passed'
|
||||
|
@ -211,25 +220,36 @@ export const bsearchFindingsHandler = (findings: CspFinding[]) =>
|
|||
filter[0]?.bool?.should?.[0]?.term?.['rule.section']?.value !== undefined;
|
||||
|
||||
if (hasRuleSectionQuerySearchTerm) {
|
||||
const filteredFindingJson = findings.filter((finding) => {
|
||||
const filteredFindings = findings.filter((finding) => {
|
||||
const termValue = (filter[0].bool?.should as estypes.QueryDslQueryContainer[])?.[0]?.term?.[
|
||||
'rule.section'
|
||||
]?.value;
|
||||
return finding.rule.section === termValue;
|
||||
});
|
||||
|
||||
return HttpResponse.json(getFindingsBsearchResponse(filteredFindingJson));
|
||||
return HttpResponse.json(getFindingsBsearchResponse(filteredFindings));
|
||||
}
|
||||
|
||||
const hasRuleSectionFilter =
|
||||
isArray(filter) && filter?.[0]?.match_phrase?.['rule.section'] !== undefined;
|
||||
|
||||
if (hasRuleSectionFilter) {
|
||||
const filteredFindingJson = findings.filter((finding) => {
|
||||
const filteredFindings = findings.filter((finding) => {
|
||||
return finding.rule.section === filter?.[0]?.match_phrase?.['rule.section'];
|
||||
});
|
||||
|
||||
return HttpResponse.json(getFindingsBsearchResponse(filteredFindingJson));
|
||||
return HttpResponse.json(getFindingsBsearchResponse(filteredFindings));
|
||||
}
|
||||
|
||||
const hasResultEvaluationFilter =
|
||||
isArray(filter) && filter?.[0]?.match_phrase?.['result.evaluation'] !== undefined;
|
||||
|
||||
if (hasResultEvaluationFilter) {
|
||||
const filteredFindings = findings.filter((finding) => {
|
||||
return finding.result.evaluation === filter?.[0]?.match_phrase?.['result.evaluation'];
|
||||
});
|
||||
|
||||
return HttpResponse.json(getFindingsBsearchResponse(filteredFindings));
|
||||
}
|
||||
|
||||
return HttpResponse.json(getFindingsBsearchResponse(findings));
|
||||
|
|
|
@ -22,6 +22,7 @@ import * as statusHandlers from '../../../server/routes/status/status.handlers.m
|
|||
import {
|
||||
bsearchFindingsHandler,
|
||||
generateCspFinding,
|
||||
generateMultipleCspFindings,
|
||||
rulesGetStatesHandler,
|
||||
} from './configurations.handlers.mock';
|
||||
|
||||
|
@ -247,4 +248,109 @@ describe('<Findings />', () => {
|
|||
expect(screen.getByText(finding2.resource.name)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('DistributionBar', () => {
|
||||
it('renders the distribution bar', async () => {
|
||||
server.use(statusHandlers.indexedHandler);
|
||||
server.use(
|
||||
bsearchFindingsHandler(
|
||||
generateMultipleCspFindings({
|
||||
count: 10,
|
||||
failedCount: 3,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
renderFindingsPage();
|
||||
|
||||
// Loading while checking the status API
|
||||
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/10 findings/i)).toBeInTheDocument());
|
||||
|
||||
screen.getByRole('button', {
|
||||
name: /passed findings: 7/i,
|
||||
});
|
||||
screen.getByRole('button', {
|
||||
name: /failed findings: 3/i,
|
||||
});
|
||||
|
||||
// Assert that the distribution bar has the correct percentages rendered
|
||||
expect(screen.getByTestId('distribution_bar_passed')).toHaveStyle('flex: 7');
|
||||
expect(screen.getByTestId('distribution_bar_failed')).toHaveStyle('flex: 3');
|
||||
});
|
||||
|
||||
it('filters by passed findings when clicking on the passed findings button', async () => {
|
||||
server.use(statusHandlers.indexedHandler);
|
||||
server.use(
|
||||
bsearchFindingsHandler(
|
||||
generateMultipleCspFindings({
|
||||
count: 2,
|
||||
failedCount: 1,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
renderFindingsPage();
|
||||
|
||||
// Loading while checking the status API
|
||||
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/2 findings/i)).toBeInTheDocument());
|
||||
|
||||
const passedFindingsButton = screen.getByRole('button', {
|
||||
name: /passed findings: 1/i,
|
||||
});
|
||||
userEvent.click(passedFindingsButton);
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/1 findings/i)).toBeInTheDocument());
|
||||
|
||||
screen.getByRole('button', {
|
||||
name: /passed findings: 1/i,
|
||||
});
|
||||
screen.getByRole('button', {
|
||||
name: /failed findings: 0/i,
|
||||
});
|
||||
|
||||
// Assert that the distribution bar has the correct percentages rendered
|
||||
expect(screen.getByTestId('distribution_bar_passed')).toHaveStyle('flex: 1');
|
||||
expect(screen.getByTestId('distribution_bar_failed')).toHaveStyle('flex: 0');
|
||||
}, 10000);
|
||||
it('filters by failed findings when clicking on the failed findings button', async () => {
|
||||
server.use(statusHandlers.indexedHandler);
|
||||
server.use(
|
||||
bsearchFindingsHandler(
|
||||
generateMultipleCspFindings({
|
||||
count: 2,
|
||||
failedCount: 1,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
renderFindingsPage();
|
||||
|
||||
// Loading while checking the status API
|
||||
expect(screen.getByText(/loading/i)).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/2 findings/i)).toBeInTheDocument());
|
||||
|
||||
const failedFindingsButton = screen.getByRole('button', {
|
||||
name: /failed findings: 1/i,
|
||||
});
|
||||
userEvent.click(failedFindingsButton);
|
||||
|
||||
await waitFor(() => expect(screen.getByText(/1 findings/i)).toBeInTheDocument());
|
||||
|
||||
screen.getByRole('button', {
|
||||
name: /passed findings: 0/i,
|
||||
});
|
||||
screen.getByRole('button', {
|
||||
name: /failed findings: 1/i,
|
||||
});
|
||||
|
||||
// Assert that the distribution bar has the correct percentages rendered
|
||||
expect(screen.getByTestId('distribution_bar_passed')).toHaveStyle('flex: 0');
|
||||
expect(screen.getByTestId('distribution_bar_failed')).toHaveStyle('flex: 1');
|
||||
}, 10000);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
EuiBadge,
|
||||
EuiSpacer,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
useEuiTheme,
|
||||
EuiTextColor,
|
||||
} from '@elastic/eui';
|
||||
|
@ -28,6 +27,14 @@ interface Props {
|
|||
distributionOnClick: (evaluation: Evaluation) => void;
|
||||
}
|
||||
|
||||
const I18N_PASSED_FINDINGS = i18n.translate('xpack.csp.findings.distributionBar.totalPassedLabel', {
|
||||
defaultMessage: 'Passed Findings',
|
||||
});
|
||||
|
||||
const I18N_FAILED_FINDINGS = i18n.translate('xpack.csp.findings.distributionBar.totalFailedLabel', {
|
||||
defaultMessage: 'Failed Findings',
|
||||
});
|
||||
|
||||
export const CurrentPageOfTotal = ({
|
||||
pageEnd,
|
||||
pageStart,
|
||||
|
@ -60,42 +67,21 @@ export const FindingsDistributionBar = (props: Props) => (
|
|||
<DistributionBar {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const Counters = (props: Props) => (
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup justifyContent="flexEnd">
|
||||
<PassedFailedCounters {...props} />
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
||||
const PassedFailedCounters = ({ passed, failed }: Pick<Props, 'passed' | 'failed'>) => {
|
||||
const Counters = ({ passed, failed }: Pick<Props, 'passed' | 'failed'>) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
return (
|
||||
<div
|
||||
<EuiFlexGroup
|
||||
justifyContent="flexEnd"
|
||||
css={css`
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
grid-column-gap: ${euiTheme.size.m};
|
||||
gap: ${euiTheme.size.m};
|
||||
`}
|
||||
>
|
||||
<Counter
|
||||
label={i18n.translate('xpack.csp.findings.distributionBar.totalPassedLabel', {
|
||||
defaultMessage: 'Passed Findings',
|
||||
})}
|
||||
color={statusColors.passed}
|
||||
value={passed}
|
||||
/>
|
||||
<Counter
|
||||
label={i18n.translate('xpack.csp.findings.distributionBar.totalFailedLabel', {
|
||||
defaultMessage: 'Failed Findings',
|
||||
})}
|
||||
color={statusColors.failed}
|
||||
value={failed}
|
||||
/>
|
||||
</div>
|
||||
<EuiHealth color={statusColors.passed}>{I18N_PASSED_FINDINGS}</EuiHealth>
|
||||
<EuiBadge>{getAbbreviatedNumber(passed)}</EuiBadge>
|
||||
<EuiHealth color={statusColors.failed}>{I18N_FAILED_FINDINGS}</EuiHealth>
|
||||
<EuiBadge>{getAbbreviatedNumber(failed)}</EuiBadge>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -121,6 +107,7 @@ const DistributionBar: React.FC<Omit<Props, 'pageEnd' | 'pageStart'>> = ({
|
|||
distributionOnClick(RULE_PASSED);
|
||||
}}
|
||||
data-test-subj="distribution_bar_passed"
|
||||
aria-label={`${I18N_PASSED_FINDINGS}: ${passed}`}
|
||||
/>
|
||||
<DistributionBarPart
|
||||
value={failed}
|
||||
|
@ -129,6 +116,7 @@ const DistributionBar: React.FC<Omit<Props, 'pageEnd' | 'pageStart'>> = ({
|
|||
distributionOnClick(RULE_FAILED);
|
||||
}}
|
||||
data-test-subj="distribution_bar_failed"
|
||||
aria-label={`${I18N_FAILED_FINDINGS}: ${failed}`}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
@ -144,25 +132,18 @@ const DistributionBarPart = ({
|
|||
color: string;
|
||||
distributionOnClick: () => void;
|
||||
['data-test-subj']: string;
|
||||
['aria-label']: string;
|
||||
}) => (
|
||||
<button
|
||||
data-test-subj={rest['data-test-subj']}
|
||||
aria-label={rest['aria-label']}
|
||||
onClick={distributionOnClick}
|
||||
css={css`
|
||||
flex: ${value};
|
||||
background: ${color};
|
||||
height: 100%;
|
||||
`}
|
||||
css={{
|
||||
background: color,
|
||||
height: '100%',
|
||||
}}
|
||||
style={{
|
||||
flex: value,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const Counter = ({ label, value, color }: { label: string; value: number; color: string }) => (
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={1}>
|
||||
<EuiHealth color={color}>{label}</EuiHealth>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge>{getAbbreviatedNumber(value)}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -17,7 +17,6 @@ import type { FtrProviderContext } from '../ftr_provider_context';
|
|||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const filterBar = getService('filterBar');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const supertest = getService('supertest');
|
||||
|
@ -189,19 +188,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
describe('DistributionBar', () => {
|
||||
(['passed', 'failed'] as const).forEach((type) => {
|
||||
it(`filters by ${type} findings`, async () => {
|
||||
await distributionBar.filterBy(type);
|
||||
|
||||
const items = data.filter(({ result }) => result.evaluation === type);
|
||||
expect(await latestFindingsTable.getFindingsCount(type)).to.eql(items.length);
|
||||
|
||||
await filterBar.removeFilter('result.evaluation');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Findings - Fields selector', () => {
|
||||
const CSP_FIELDS_SELECTOR_MODAL = 'cloudSecurityFieldsSelectorModal';
|
||||
const CSP_FIELDS_SELECTOR_OPEN_BUTTON = 'cloudSecurityFieldsSelectorOpenButton';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue