Skip to content

Commit

Permalink
[Cloud Posture] [Dashboard] Add sorting by Compliance Score in the cl…
Browse files Browse the repository at this point in the history
…usters section (elastic#149566)

Issue elastic#144013

## Summary

This PR adds the capability to sort Clusters by Compliance Score in the
Cloud Posture Dashboard. It saves the sorting on the Local Storage and
defaults to **Descending** on the initial load.


The following changes were also introduced:

- Added unit tests for sorting
- Also added missing tests for the clusters section columns and added
some tests to do in future PRs
- Isolated `mockDashboardData` into own mock file and added a
`clusterMockData`
- Styling optimizations
- Wrapped existent callbacks into `useCallback`

## Screenshots

### Initial load


![image](https://user-images.githubusercontent.com/19270322/215618280-65415866-e8fb-4df5-85bc-4ea7af0ce375.png)


### Sort Toggle


![image](https://user-images.githubusercontent.com/19270322/215618358-520fe047-0624-4c87-8e99-998ae18610fa.png)

### Tests


![image](https://user-images.githubusercontent.com/19270322/215618537-ce064460-71b4-48ef-9594-ce1093a94157.png)
  • Loading branch information
opauloh authored and kqualters-elastic committed Feb 6, 2023
1 parent 75fc86f commit 1b2d40c
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 246 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const DEFAULT_VISIBLE_ROWS_PER_PAGE = 25;
export const LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY = 'cloudPosture:findings:pageSize';
export const LOCAL_STORAGE_PAGE_SIZE_BENCHMARK_KEY = 'cloudPosture:benchmark:pageSize';
export const LOCAL_STORAGE_PAGE_SIZE_RULES_KEY = 'cloudPosture:rules:pageSize';
export const LOCAL_STORAGE_DASHBOARD_CLUSTER_SORT_KEY =
'cloudPosture:complianceDashboard:clusterSort';

export type CloudPostureIntegrations = Record<PosturePolicyTemplate, CloudPostureIntegrationProps>;
export interface CloudPostureIntegrationProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {
DASHBOARD_CONTAINER,
KUBERNETES_DASHBOARD_CONTAINER,
} from './test_subjects';
import { mockDashboardData } from './mock';
import { createReactQueryResponse } from '../../test/fixtures/react_query';
import { NO_FINDINGS_STATUS_TEST_SUBJ } from '../../components/test_subjects';
import { useCISIntegrationPoliciesLink } from '../../common/navigation/use_navigate_to_cis_integration_policies';
import { useCspIntegrationLink } from '../../common/navigation/use_csp_integration_link';
import { expectIdsInDoc } from '../../test/utils';
import { ComplianceDashboardData } from '../../../common/types';

jest.mock('../../common/api/use_setup_status_api');
jest.mock('../../common/api/use_stats_api');
Expand All @@ -34,166 +34,6 @@ jest.mock('../../common/navigation/use_csp_integration_link');

const chance = new Chance();

export const mockDashboardData: ComplianceDashboardData = {
stats: {
totalFailed: 17,
totalPassed: 155,
totalFindings: 172,
postureScore: 90.1,
resourcesEvaluated: 162,
},
groupedFindingsEvaluation: [
{
name: 'RBAC and Service Accounts',
totalFindings: 104,
totalFailed: 0,
totalPassed: 104,
postureScore: 100,
},
{
name: 'API Server',
totalFindings: 27,
totalFailed: 11,
totalPassed: 16,
postureScore: 59.2,
},
{
name: 'Master Node Configuration Files',
totalFindings: 17,
totalFailed: 1,
totalPassed: 16,
postureScore: 94.1,
},
{
name: 'Kubelet',
totalFindings: 11,
totalFailed: 4,
totalPassed: 7,
postureScore: 63.6,
},
{
name: 'etcd',
totalFindings: 6,
totalFailed: 0,
totalPassed: 6,
postureScore: 100,
},
{
name: 'Worker Node Configuration Files',
totalFindings: 5,
totalFailed: 0,
totalPassed: 5,
postureScore: 100,
},
{
name: 'Scheduler',
totalFindings: 2,
totalFailed: 1,
totalPassed: 1,
postureScore: 50.0,
},
],
clusters: [
{
meta: {
clusterId: '8f9c5b98-cc02-4827-8c82-316e2cc25870',
benchmarkName: 'CIS Kubernetes V1.20',
lastUpdate: '2022-11-07T13:14:34.990Z',
benchmarkId: 'cis_k8s',
},
stats: {
totalFailed: 17,
totalPassed: 155,
totalFindings: 172,
postureScore: 90.1,
},
groupedFindingsEvaluation: [
{
name: 'RBAC and Service Accounts',
totalFindings: 104,
totalFailed: 0,
totalPassed: 104,
postureScore: 100,
},
{
name: 'API Server',
totalFindings: 27,
totalFailed: 11,
totalPassed: 16,
postureScore: 59.2,
},
{
name: 'Master Node Configuration Files',
totalFindings: 17,
totalFailed: 1,
totalPassed: 16,
postureScore: 94.1,
},
{
name: 'Kubelet',
totalFindings: 11,
totalFailed: 4,
totalPassed: 7,
postureScore: 63.6,
},
{
name: 'etcd',
totalFindings: 6,
totalFailed: 0,
totalPassed: 6,
postureScore: 100,
},
{
name: 'Worker Node Configuration Files',
totalFindings: 5,
totalFailed: 0,
totalPassed: 5,
postureScore: 100,
},
{
name: 'Scheduler',
totalFindings: 2,
totalFailed: 1,
totalPassed: 1,
postureScore: 50.0,
},
],
trend: [
{
timestamp: '2022-05-22T11:03:00.000Z',
totalFindings: 172,
totalFailed: 17,
totalPassed: 155,
postureScore: 90.1,
},
{
timestamp: '2022-05-22T10:25:00.000Z',
totalFindings: 172,
totalFailed: 17,
totalPassed: 155,
postureScore: 90.1,
},
],
},
],
trend: [
{
timestamp: '2022-05-22T11:03:00.000Z',
totalFindings: 172,
totalFailed: 17,
totalPassed: 155,
postureScore: 90.1,
},
{
timestamp: '2022-05-22T10:25:00.000Z',
totalFindings: 172,
totalFailed: 17,
totalPassed: 155,
postureScore: 90.1,
},
],
};

describe('<ComplianceDashboard />', () => {
beforeEach(() => {
jest.resetAllMocks();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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 userEvent from '@testing-library/user-event';
import { BenchmarksSection } from './benchmarks_section';
import { getMockDashboardData, getClusterMockData } from '../mock';
import { TestProvider } from '../../../test/test_provider';
import { KSPM_POLICY_TEMPLATE } from '../../../../common/constants';
import {
DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID,
DASHBOARD_TABLE_HEADER_SCORE_TEST_ID,
} from '../../findings/test_subjects';

describe('<BenchmarksSection />', () => {
const renderBenchmarks = (alterMockData = {}) =>
render(
<TestProvider>
<BenchmarksSection
complianceData={{ ...getMockDashboardData(), ...alterMockData }}
dashboardType={KSPM_POLICY_TEMPLATE}
/>
</TestProvider>
);

describe('Sorting', () => {
const mockDashboardDataCopy = getMockDashboardData();
const clusterMockDataCopy = getClusterMockData();
clusterMockDataCopy.stats.postureScore = 50;
clusterMockDataCopy.meta.clusterId = '1';

const clusterMockDataCopy1 = getClusterMockData();
clusterMockDataCopy1.stats.postureScore = 95;
clusterMockDataCopy1.meta.clusterId = '2';

const clusterMockDataCopy2 = getClusterMockData();
clusterMockDataCopy2.stats.postureScore = 45;
clusterMockDataCopy2.meta.clusterId = '3';

mockDashboardDataCopy.clusters = [
clusterMockDataCopy,
clusterMockDataCopy1,
clusterMockDataCopy2,
];

it('sorts by ascending order of compliance scores', () => {
const { getAllByTestId } = renderBenchmarks(mockDashboardDataCopy);
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[0]).toHaveTextContent('45');
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[1]).toHaveTextContent('50');
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[2]).toHaveTextContent('95');
});

it('toggles sort order when clicking Compliance Score', () => {
const { getAllByTestId, getByTestId } = renderBenchmarks(mockDashboardDataCopy);

userEvent.click(getByTestId(DASHBOARD_TABLE_HEADER_SCORE_TEST_ID));

expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[0]).toHaveTextContent('95');
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[1]).toHaveTextContent('50');
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[2]).toHaveTextContent('45');

userEvent.click(getByTestId(DASHBOARD_TABLE_HEADER_SCORE_TEST_ID));

expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[0]).toHaveTextContent('45');
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[1]).toHaveTextContent('50');
expect(getAllByTestId(DASHBOARD_TABLE_COLUMN_SCORE_TEST_ID)[2]).toHaveTextContent('95');
});
});
});
Loading

0 comments on commit 1b2d40c

Please sign in to comment.