diff --git a/x-pack/packages/kbn-cloud-security-posture/public/src/constants/component_constants.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/constants/component_constants.ts index 04a47f0fc12a1..d4d436e981cc4 100644 --- a/x-pack/packages/kbn-cloud-security-posture/public/src/constants/component_constants.ts +++ b/x-pack/packages/kbn-cloud-security-posture/public/src/constants/component_constants.ts @@ -9,4 +9,5 @@ import { euiThemeVars } from '@kbn/ui-theme'; export const statusColors = { passed: euiThemeVars.euiColorSuccess, failed: euiThemeVars.euiColorVis9, + unknown: euiThemeVars.euiColorLightShade, }; diff --git a/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.test.tsx new file mode 100644 index 0000000000000..166fb1184e0b9 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.test.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { ComplianceScoreBar } from './compliance_score_bar'; +import { + COMPLIANCE_SCORE_BAR_UNKNOWN, + COMPLIANCE_SCORE_BAR_PASSED, + COMPLIANCE_SCORE_BAR_FAILED, +} from './test_subjects'; + +describe('', () => { + it('should display 0% compliance score with status unknown when both passed and failed are 0', () => { + render(); + expect(screen.getByText('0%')).toBeInTheDocument(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_UNKNOWN)).not.toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_FAILED)).toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_PASSED)).toBeNull(); + }); + + it('should display 100% compliance score when passed is greater than 0 and failed is 0', () => { + render(); + expect(screen.getByText('100%')).toBeInTheDocument(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_PASSED)).not.toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_FAILED)).toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_UNKNOWN)).toBeNull(); + }); + + it('should display 0% compliance score when passed is 0 and failed is greater than 0', () => { + render(); + expect(screen.getByText('0%')).toBeInTheDocument(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_FAILED)).not.toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_PASSED)).toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_UNKNOWN)).toBeNull(); + }); + + it('should display 50% compliance score when passed is equal to failed', () => { + render(); + expect(screen.getByText('50%')).toBeInTheDocument(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_FAILED)).not.toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_PASSED)).not.toBeNull(); + expect(screen.queryByTestId(COMPLIANCE_SCORE_BAR_UNKNOWN)).toBeNull(); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.tsx b/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.tsx index d4acbc97ab10c..3829542829909 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/compliance_score_bar.tsx @@ -11,7 +11,12 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { statusColors } from '@kbn/cloud-security-posture'; import { calculatePostureScore } from '../../common/utils/helpers'; -import { CSP_FINDINGS_COMPLIANCE_SCORE } from './test_subjects'; +import { + CSP_FINDINGS_COMPLIANCE_SCORE, + COMPLIANCE_SCORE_BAR_UNKNOWN, + COMPLIANCE_SCORE_BAR_FAILED, + COMPLIANCE_SCORE_BAR_PASSED, +} from './test_subjects'; /** * This component will take 100% of the width set by the parent @@ -59,12 +64,22 @@ export const ComplianceScoreBar = ({ gap: 1px; `} > + {!totalPassed && !totalFailed && ( + + )} {!!totalPassed && ( )} {!!totalFailed && ( @@ -73,6 +88,7 @@ export const ComplianceScoreBar = ({ flex: ${totalFailed}; background: ${statusColors.failed}; `} + data-test-subj={COMPLIANCE_SCORE_BAR_FAILED} /> )} diff --git a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts index d29971d3352e3..b609950720ecd 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts @@ -92,3 +92,7 @@ export const CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS = { }; export const SUBSCRIPTION_NOT_ALLOWED_TEST_SUBJECT = 'cloud_posture_page_subscription_not_allowed'; + +export const COMPLIANCE_SCORE_BAR_UNKNOWN = 'complianceScoreBarUnknown'; +export const COMPLIANCE_SCORE_BAR_FAILED = 'complianceScoreBarFailed'; +export const COMPLIANCE_SCORE_BAR_PASSED = 'complianceScoreBarPassed'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.test.tsx new file mode 100644 index 0000000000000..60aa64aa88141 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.test.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { render } from '@testing-library/react'; +import { useEuiTheme } from '@elastic/eui'; +import { ComplianceBarComponent } from './latest_findings_group_renderer'; +import { RawBucket } from '@kbn/grouping/src'; +import { FindingsGroupingAggregation } from './use_grouped_findings'; +import { ComplianceScoreBar } from '../../../components/compliance_score_bar'; + +jest.mock('@elastic/eui', () => { + const actual = jest.requireActual('@elastic/eui'); + return { + ...actual, + useEuiTheme: jest.fn(), + }; +}); + +jest.mock('../../../components/compliance_score_bar', () => ({ + ComplianceScoreBar: jest.fn(() => null), +})); + +jest.mock('../../../components/cloud_security_grouping'); + +describe('', () => { + beforeEach(() => { + (useEuiTheme as jest.Mock).mockReturnValue({ euiTheme: { size: { s: 's' } } }); + (ComplianceScoreBar as jest.Mock).mockClear(); + }); + + it('renders ComplianceScoreBar with correct totalFailed and totalPassed, when total = failed+passed', () => { + const bucket = { + doc_count: 10, + failedFindings: { + doc_count: 4, + }, + passedFindings: { + doc_count: 6, + }, + } as RawBucket; + + render(); + + expect(ComplianceScoreBar).toHaveBeenCalledWith( + expect.objectContaining({ + totalFailed: 4, + totalPassed: 6, + }), + {} + ); + }); + + it('renders ComplianceScoreBar with correct totalFailed and totalPassed, when there are unknown findings', () => { + const bucket = { + doc_count: 10, + failedFindings: { + doc_count: 3, + }, + passedFindings: { + doc_count: 6, + }, + } as RawBucket; + + render(); + + expect(ComplianceScoreBar).toHaveBeenCalledWith( + expect.objectContaining({ + totalFailed: 3, + totalPassed: 6, + }), + {} + ); + }); + + it('renders ComplianceScoreBar with correct totalFailed and totalPassed, when there are no findings', () => { + const bucket = { + doc_count: 10, + failedFindings: { + doc_count: 0, + }, + passedFindings: { + doc_count: 0, + }, + } as RawBucket; + + render(); + + expect(ComplianceScoreBar).toHaveBeenCalledWith( + expect.objectContaining({ + totalFailed: 0, + totalPassed: 0, + }), + {} + ); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx index b4ad5d15ec8e9..b41c5e4996db1 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx @@ -198,11 +198,15 @@ const FindingsCountComponent = ({ bucket }: { bucket: RawBucket }) => { +export const ComplianceBarComponent = ({ + bucket, +}: { + bucket: RawBucket; +}) => { const { euiTheme } = useEuiTheme(); const totalFailed = bucket.failedFindings?.doc_count || 0; - const totalPassed = bucket.doc_count - totalFailed; + const totalPassed = bucket.passedFindings?.doc_count || 0; return ( ; - numberOfPassedFindings?: number; - numberOfFailedFindings?: number; }) => { return (