diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 955fe7a72414af..c9a97dd80e4aa9 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -23,6 +23,7 @@ disabled: - x-pack/test/upgrade/config.ts - test/functional/config.edge.js - x-pack/test/functional/config.edge.js + - x-pack/test/cloud_security_posture_functional/config.cloud.ts # Cypress configs, for now these are still run manually - x-pack/test/fleet_cypress/cli_config.ts diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/README.md b/x-pack/test/cloud_security_posture_functional/cloud_tests/README.md new file mode 100644 index 00000000000000..ce67cefa1022aa --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/README.md @@ -0,0 +1,24 @@ +## Tests Development Process + +**Preparation** + +- **Environment Deployment** - Initially, to start tests development, deploy the environment using the [Create Environment](https://github.com/elastic/cloudbeat/blob/main/dev-docs/Cloud-Env-Testing.md) workflow. + +- **Configuration & Run** - After provisioning the environment, configure the FTR environment variables accordingly. At a minimum, configure the following variables: TEST_KIBANA_URL, TEST_ES_URL, TEST_CLOUD, ES_SECURITY_ENABLED. More information can be found in the [FTR documentation](https://www.elastic.co/guide/en/kibana/current/development-tests.html#development-functional-tests). Note that URLs should contain the user and password. + +``` bash +export TEST_KIBANA_URL=https://elastic:password@my-kbn-cluster.elastic-cloud.com:443 +export TEST_ES_URL=https://elastic:password@my-es-cluster.elastic-cloud.com:443 + +export TEST_CLOUD=1 +export ES_SECURITY_ENABLED=1 + +node scripts/functional_test_runner [--config ] [--es-version ] +``` + +- **Suite Config** - The config file is located here: `x-pack/test/cloud_security_posture_functional/config.cloud.ts`. + + +**Final Testing on Demand** + +After finishing the implementation and ensuring that the whole suite is working locally, proceed to execution in the Create Environment workflow. [This PR](https://github.com/elastic/cloudbeat/pull/2219) adds the feature to run UI tests. To do this, open a PR in Kibana, then find the PR commit and apply it in the Create Environment workflow for the final end-to-end process. This process takes time, so proceed only after a few local runs to ensure test stability. \ No newline at end of file diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/basic_ui_sanity.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/basic_ui_sanity.ts new file mode 100644 index 00000000000000..495105017e5c22 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/basic_ui_sanity.ts @@ -0,0 +1,75 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const retry = getService('retry'); + const pageObjects = getPageObjects(['common', 'cloudPostureDashboard', 'header']); + + describe('Cloud Posture Dashboard Page', function () { + this.tags(['cloud_security_posture_ui_sanity']); + let cspDashboard: typeof pageObjects.cloudPostureDashboard; + let dashboard: typeof pageObjects.cloudPostureDashboard.dashboard; + + before(async () => { + cspDashboard = pageObjects.cloudPostureDashboard; + dashboard = pageObjects.cloudPostureDashboard.dashboard; + await cspDashboard.waitForPluginInitialized(); + await cspDashboard.navigateToComplianceDashboardPage(); + await retry.waitFor( + 'Cloud posture integration dashboard to be displayed', + async () => !!dashboard.getIntegrationDashboardContainer() + ); + }); + + describe('Cloud Dashboard', () => { + it('displays compliance score greater than 40', async () => { + await pageObjects.header.waitUntilLoadingHasFinished(); + const scoreElement = await dashboard.getCloudComplianceScore(); + const score = parseInt((await scoreElement.getVisibleText()).replace('%', ''), 10); + expect(score).to.be.greaterThan(40); + }); + + it('displays all compliance scores', async () => { + const scoresElements = await dashboard.getAllCloudComplianceScores(); + const scores: string[] = []; + for (const scoreElement of scoresElements) { + scores.push(await scoreElement.getVisibleText()); + } + // 3 scores for each cloud provider + 1 summary score + expect(scores.length).to.be(4); + }); + + it('displays a number of resources evaluated greater than 3000', async () => { + const resourcesEvaluated = await dashboard.getCloudResourcesEvaluated(); + const visibleText = await resourcesEvaluated.getVisibleText(); + const resourcesEvaluatedCount = parseInt(visibleText.replace(/,/g, ''), 10); + expect(resourcesEvaluatedCount).greaterThan(3000); + }); + }); + + describe('Kubernetes Dashboard', () => { + it('displays compliance score greater than 80', async () => { + await pageObjects.header.waitUntilLoadingHasFinished(); + const scoreElement = await dashboard.getKubernetesComplianceScore(); + const score = parseInt((await scoreElement.getVisibleText()).replace('%', ''), 10); + expect(score).to.be.greaterThan(80); + }); + + it('displays a number of resources evaluated greater than 150', async () => { + const resourcesEvaluated = await dashboard.getKubernetesResourcesEvaluated(); + const resourcesEvaluatedCount = parseInt( + (await resourcesEvaluated.getVisibleText()).replace(/,/g, ''), + 10 + ); + expect(resourcesEvaluatedCount).greaterThan(150); + }); + }); + }); +}; diff --git a/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts b/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts new file mode 100644 index 00000000000000..b08970ccaed13f --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/cloud_tests/index.ts @@ -0,0 +1,15 @@ +/* + * 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 { FtrProviderContext } from '../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Cloud Security Posture', function () { + loadTestFile(require.resolve('./basic_ui_sanity')); + }); +} diff --git a/x-pack/test/cloud_security_posture_functional/config.cloud.ts b/x-pack/test/cloud_security_posture_functional/config.cloud.ts new file mode 100644 index 00000000000000..1fc18baeba7434 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/config.cloud.ts @@ -0,0 +1,25 @@ +/* + * 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 { resolve } from 'path'; +import type { FtrConfigProviderContext } from '@kbn/test'; +import { pageObjects } from './page_objects'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const xpackFunctionalConfig = await readConfigFile( + require.resolve('../functional/config.base.js') + ); + // FTR configuration for cloud testing + return { + ...xpackFunctionalConfig.getAll(), + pageObjects, + testFiles: [resolve(__dirname, './cloud_tests')], + junit: { + reportName: 'X-Pack Cloud Security Posture Sanity Tests', + }, + }; +} diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts index 24a89549fb025b..4343662e32efdb 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/csp_dashboard_page.ts @@ -96,11 +96,26 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv return await testSubjects.find('dashboard-summary-section'); }, + getAllCloudComplianceScores: async () => { + await dashboard.getCloudDashboard(); + return await testSubjects.findAll('dashboard-summary-section-compliance-score'); + }, + getCloudComplianceScore: async () => { await dashboard.getCloudSummarySection(); return await testSubjects.find('dashboard-summary-section-compliance-score'); }, + getCloudResourcesEvaluatedCard: async () => { + await dashboard.getCloudDashboard(); + return await testSubjects.find('dashboard-counter-card-resources-evaluated'); + }, + + getCloudResourcesEvaluated: async () => { + const resourcesEvaluatedCard = await dashboard.getCloudResourcesEvaluatedCard(); + return await resourcesEvaluatedCard.findByXpath('//div/p/span'); + }, + // Kubernetes Dashboard getKubernetesDashboard: async () => { @@ -121,6 +136,16 @@ export function CspDashboardPageProvider({ getService, getPageObjects }: FtrProv return await testSubjects.find('dashboard-summary-section-compliance-score'); }, + + getKubernetesResourcesEvaluatedCard: async () => { + await dashboard.getKubernetesDashboard(); + return await testSubjects.find('dashboard-counter-card-resources-evaluated'); + }, + + getKubernetesResourcesEvaluated: async () => { + const resourcesEvaluatedCard = await dashboard.getKubernetesResourcesEvaluatedCard(); + return await resourcesEvaluatedCard.findByXpath('//div/p/span'); + }, }; const navigateToComplianceDashboardPage = async () => {