diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture/__tests__/mockData.ts b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture/__tests__/mockData.ts new file mode 100644 index 0000000000000..9c6ef9474191b --- /dev/null +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture/__tests__/mockData.ts @@ -0,0 +1,160 @@ +/* + * 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 type { MonitoringType } from '../../../../../../../../../../common/types'; + +export const mockAgentPolicy = { + id: '888c9a80-cae6-4675-82ad-73553cb343f9', + name: 'Agent policy 1', + description: '', + namespace: 'default', + monitoring_enabled: ['logs', 'metrics', 'traces'] as MonitoringType, + inactivity_timeout: 1209600, + is_protected: false, + status: 'active' as const, // or 'Inactive' as const + is_managed: false, + revision: 1, + updated_at: '2025-01-29T14:12:36.256Z', + updated_by: 'elastic', + schema_version: '1.1.1', +}; + +export const mockPackagePolicy = { + id: '5811fb40-82e4-4808-b94e-5e02158936c7', + version: 'WzgyMSwxXQ==', + name: 'cspm-1', + namespace: 'default', + description: '', + package: { + name: 'cloud_security_posture', + title: 'Security Posture Management', + version: '1.12.0', + }, + enabled: true, + policy_id: '888c9a80-cae6-4675-82ad-73553cb343f9', + policy_ids: ['888c9a80-cae6-4675-82ad-73553cb343f9'], + inputs: [ + { + type: 'cloudbeat/cis_k8s', + policy_template: 'kspm', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { + type: 'logs', + dataset: 'cloud_security_posture.findings', + }, + vars: { + condition: { + type: 'text', + }, + }, + id: 'cloudbeat/cis_k8s-cloud_security_posture.findings-5811fb40-82e4-4808-b94e-5e02158936c7', + }, + ], + }, + { + type: 'cloudbeat/cis_eks', + policy_template: 'kspm', + enabled: false, + streams: [ + { + enabled: false, + data_stream: { + type: 'logs', + dataset: 'cloud_security_posture.findings', + }, + vars: { + condition: { type: 'text' }, + access_key_id: { type: 'text' }, + secret_access_key: { type: 'password' }, + session_token: { type: 'text' }, + shared_credential_file: { type: 'text' }, + credential_profile_name: { type: 'text' }, + role_arn: { type: 'text' }, + aws_credentials_type: { type: 'text' }, + }, + id: 'cloudbeat/cis_eks-cloud_security_posture.findings-5811fb40-82e4-4808-b94e-5e02158936c7', + }, + ], + }, + { + type: 'cloudbeat/cis_aws', + policy_template: 'cspm', + enabled: true, + streams: [ + { + enabled: true, + data_stream: { + type: 'logs', + dataset: 'cloud_security_posture.findings', + }, + vars: { + condition: { type: 'text' }, + access_key_id: { type: 'text' }, + secret_access_key: { type: 'password' }, + session_token: { type: 'text' }, + shared_credential_file: { type: 'text' }, + credential_profile_name: { type: 'text' }, + role_arn: { type: 'text' }, + aws_credentials_type: { value: 'cloud_formation', type: 'text' }, + aws_account_type: { value: 'organization-account', type: 'text' }, + aws_supports_cloud_connectors: { type: 'bool' }, + }, + id: 'cloudbeat/cis_aws-cloud_security_posture.findings-5811fb40-82e4-4808-b94e-5e02158936c7', + compiled_stream: { + period: '24h', + fetchers: [ + { name: 'aws-iam' }, + { name: 'aws-ec2-network' }, + { name: 'aws-s3' }, + { name: 'aws-trail' }, + { name: 'aws-monitoring' }, + { name: 'aws-rds' }, + ], + config: { + v1: { + type: 'cspm', + deployment: 'aws', + benchmark: 'cis_aws', + aws: { + account_type: 'organization-account', + credentials: { + type: 'cloud_formation', + }, + }, + }, + }, + }, + }, + ], + vars: { + cloud_formation_template: { + value: + 'https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateURL=https://elastic-cspm-cft.s3.eu-central-1.amazonaws.com/cloudformation-cspm-ACCOUNT_TYPE-8.17.0.yml&stackName=Elastic-Cloud-Security-Posture-Management¶m_EnrollmentToken=FLEET_ENROLLMENT_TOKEN¶m_FleetUrl=FLEET_URL¶m_ElasticAgentVersion=KIBANA_VERSION¶m_ElasticArtifactServer=https://artifacts.elastic.co/downloads/beats/elastic-agent', + type: 'text', + }, + }, + config: { + cloud_formation_template_url: { + value: + 'https://console.aws.amazon.com/cloudformation/home#/stacks/quickcreate?templateURL=https://elastic-cspm-cft.s3.eu-central-1.amazonaws.com/cloudformation-cspm-ACCOUNT_TYPE-8.17.0.yml&stackName=Elastic-Cloud-Security-Posture-Management¶m_EnrollmentToken=FLEET_ENROLLMENT_TOKEN¶m_FleetUrl=FLEET_URL¶m_ElasticAgentVersion=KIBANA_VERSION¶m_ElasticArtifactServer=https://artifacts.elastic.co/downloads/beats/elastic-agent', + }, + }, + }, + ], + vars: { + posture: { value: 'cspm', type: 'text' }, + deployment: { value: 'aws', type: 'text' }, + }, + revision: 1, + created_at: '2025-01-29T14:12:41.393Z', + created_by: 'system', + updated_at: '2025-01-29T14:12:41.393Z', + updated_by: 'system', +}; diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture/__tests__/post_install_cloud_formation_modal.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture/__tests__/post_install_cloud_formation_modal.test.tsx new file mode 100644 index 0000000000000..4cf7db96facd4 --- /dev/null +++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/cloud_security_posture/__tests__/post_install_cloud_formation_modal.test.tsx @@ -0,0 +1,124 @@ +/* + * 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 { fireEvent, render, screen } from '@testing-library/react'; +import { useQuery } from '@tanstack/react-query'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; + +import { PostInstallCloudFormationModal } from '../post_install_cloud_formation_modal'; +import { useAgentPolicyWithPackagePolicies } from '../../../../../../../../../components/agent_enrollment_flyout/hooks'; +import { useFleetServerHostsForPolicy } from '../../../../../../../../../hooks'; + +import { useCreateCloudFormationUrl } from '../../../../../../../../../components/cloud_security_posture/hooks'; + +import { mockAgentPolicy, mockPackagePolicy } from './mockData'; + +jest.mock('@tanstack/react-query'); +jest.mock('../../../../../../../../../components/agent_enrollment_flyout/hooks'); +jest.mock('../../../../../../../../../hooks'); +jest.mock('../../../../../../../../../components/cloud_security_posture/hooks'); + +describe('PostInstallCloudFormationModal', () => { + const mockOnConfirm = jest.fn(); + const mockOnCancel = jest.fn(); + + beforeAll(() => { + (useAgentPolicyWithPackagePolicies as jest.Mock).mockReturnValue({ + agentPolicyWithPackagePolicies: null, + }); + + (useFleetServerHostsForPolicy as jest.Mock).mockReturnValue({ + fleetServerHost: 'https://any-hostname:8220', + isLoadingInitialRequest: false, + }); + + (useCreateCloudFormationUrl as jest.Mock).mockReturnValue({ + cloudFormationUrl: 'console.aws.amazon.com/cloudformation', + }); + }); + + it('should render the modal with confirm button enabled', () => { + (useQuery as jest.Mock).mockReturnValueOnce({ + data: { + data: { + items: [{ api_key: 'test-api-key' }], + }, + }, + isLoading: true, + }); + + render( + + + + ); + + expect(screen.getByTestId('confirmCloudFormationModalConfirmButton')).toBeInTheDocument(); + }); + + it('should render the modal with confirm button disabled', () => { + (useQuery as jest.Mock).mockReturnValueOnce({ + data: { + data: { + items: [{ api_key: 'test-api-key' }], + }, + }, + isLoading: true, + }); + + render( + + + + ); + + expect(screen.getByTestId('confirmCloudFormationModalConfirmButton')).toBeDisabled(); + }); + + it('should open correct cloudFormation URL', () => { + // Mock window.open + const mockWindowOpen = jest.fn(); + window.open = mockWindowOpen; + + (useQuery as jest.Mock).mockReturnValueOnce({ + data: { + data: { + items: [{ api_key: 'test-api-key' }], + }, + }, + isLoading: false, + }); + + render( + + + + ); + + const confirmButton = screen.getByTestId('confirmCloudFormationModalConfirmButton'); + fireEvent.click(confirmButton); + + expect(mockWindowOpen).toHaveBeenCalledTimes(1); + expect(mockWindowOpen).toHaveBeenCalledWith('console.aws.amazon.com/cloudformation'); + }); +}); diff --git a/x-pack/solutions/security/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.test.tsx b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.test.tsx new file mode 100644 index 0000000000000..8cd0f320df91b --- /dev/null +++ b/x-pack/solutions/security/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.test.tsx @@ -0,0 +1,80 @@ +/* + * 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, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { MemoryRouter } from 'react-router-dom'; +import { NoVulnerabilitiesStates } from './no_vulnerabilities_states'; +import * as useCspSetupStatusApi from '@kbn/cloud-security-posture/src/hooks/use_csp_setup_status_api'; +import * as useCspIntegrationLink from '../common/navigation/use_csp_integration_link'; +import * as useAdd3PIntegrationRoute from '../common/api/use_wiz_integration_route'; +import { + CNVM_NOT_INSTALLED_ACTION_SUBJ, + THIRD_PARTY_NO_VULNERABILITIES_FINDINGS_PROMPT_WIZ_INTEGRATION_BUTTON, +} from './test_subjects'; +import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; +import { CLOUD_SECURITY_POSTURE_BASE_PATH } from '@kbn/cloud-security-posture-common'; + +jest.mock('@kbn/cloud-security-posture/src/hooks/use_csp_setup_status_api'); +jest.mock('../common/navigation/use_csp_integration_link'); +jest.mock('../common/api/use_wiz_integration_route'); + +describe('NoVulnerabilitiesStates', () => { + const cnvmintegrationLink = 'fleet/integrations/cloud_security_posture/add-integration'; + const thirdPartyIntegrationLink = 'fleet/integrations/wiz/add-integration'; + const vulnerabilitiesPath = `${CLOUD_SECURITY_POSTURE_BASE_PATH}/findings/vulnerabilities`; + + beforeAll(() => { + (useCspIntegrationLink.useCspIntegrationLink as jest.Mock).mockReturnValue(cnvmintegrationLink); + (useAdd3PIntegrationRoute.useAdd3PIntegrationRoute as jest.Mock).mockReturnValue( + thirdPartyIntegrationLink + ); + }); + + beforeEach(() => { + (useCspSetupStatusApi.useCspSetupStatusApi as jest.Mock).mockReturnValue({ + data: { + vuln_mgmt: { status: 'not-installed' }, + indicesDetails: [], + }, + }); + + render( + + + + + + ); + }); + + it('Vulnerabilities - `Add CNVM integration`: should have link element to CNVM integration installation page', async () => { + await waitFor(() => + expect( + screen.getByText(/Elastic’s Cloud Native\s+Vulnerability Management/i) + ).toBeInTheDocument() + ); + + // Find the button + const button = screen.getByTestId(CNVM_NOT_INSTALLED_ACTION_SUBJ); + expect(button).toBeInTheDocument(); + expect(button).toHaveAttribute('href', expect.stringContaining(cnvmintegrationLink)); + }); + + it('Vulnerabilities - `Add Wiz integration`: should have link element to CNVM integration installation page', async () => { + await waitFor(() => + expect(screen.getByText(/Already using a\s+cloud security product?/i)).toBeInTheDocument() + ); + + // Find the button + const button = screen.getByTestId( + THIRD_PARTY_NO_VULNERABILITIES_FINDINGS_PROMPT_WIZ_INTEGRATION_BUTTON + ); + expect(button).toBeInTheDocument(); + expect(button).toHaveAttribute('href', expect.stringContaining(thirdPartyIntegrationLink)); + }); +}); diff --git a/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts b/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts index bd146d9a48a8c..b00902d9ba830 100644 --- a/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts +++ b/x-pack/test/cloud_security_posture_functional/agentless/create_agent.ts @@ -11,6 +11,10 @@ import equals from 'fast-deep-equal'; import { AGENTLESS_SECURITY_POSTURE_PACKAGE_VERSION } from '../constants'; import type { FtrProviderContext } from '../ftr_provider_context'; import { setupMockServer } from './mock_agentless_api'; +import { testSubjectIds } from '../constants/test_subject_ids'; + +const { CIS_AWS_OPTION_TEST_ID, AWS_SINGLE_ACCOUNT_TEST_ID } = testSubjectIds; + // eslint-disable-next-line import/no-default-export export default function ({ getPageObjects, getService }: FtrProviderContext) { const agentCreationTimeout = 1000 * 60 * 1; // 1 minute @@ -24,10 +28,6 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'cisAddIntegration', ]); - const CIS_AWS_OPTION_TEST_ID = 'cisAwsTestId'; - - const AWS_SINGLE_ACCOUNT_TEST_ID = 'awsSingleTestId'; - // Failing: See https://github.com/elastic/kibana/issues/208495 describe('Agentless cloud', function () { let cisIntegration: typeof pageObjects.cisAddIntegration; diff --git a/x-pack/test/cloud_security_posture_functional/constants/test_subject_ids.ts b/x-pack/test/cloud_security_posture_functional/constants/test_subject_ids.ts new file mode 100644 index 0000000000000..244d00bc865d5 --- /dev/null +++ b/x-pack/test/cloud_security_posture_functional/constants/test_subject_ids.ts @@ -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. + */ + +export const testSubjectIds = { + CIS_AWS_OPTION_TEST_ID: 'cisAwsTestId', + AWS_SINGLE_ACCOUNT_TEST_ID: 'awsSingleTestId', + AWS_MANUAL_TEST_ID: 'aws-manual-setup-option', + AWS_CREDENTIAL_SELECTOR: 'aws-credentials-type-selector', + ROLE_ARN_TEST_ID: 'awsRoleArnInput', + DIRECT_ACCESS_KEY_ID_TEST_ID: 'awsDirectAccessKeyId', + DIRECT_ACCESS_SECRET_KEY_TEST_ID: 'passwordInput-secret-access-key', + TEMP_ACCESS_KEY_ID_TEST_ID: 'awsTemporaryKeysAccessKeyId', + TEMP_ACCESS_KEY_SECRET_KEY_TEST_ID: 'passwordInput-secret-access-key', + TEMP_ACCESS_SESSION_TOKEN_TEST_ID: 'awsTemporaryKeysSessionToken', + SHARED_CREDENTIALS_FILE_TEST_ID: 'awsSharedCredentialFile', + SHARED_CREDETIALS_PROFILE_NAME_TEST_ID: 'awsCredentialProfileName', + CIS_GCP_OPTION_TEST_ID: 'cisGcpTestId', + GCP_ORGANIZATION_TEST_ID: 'gcpOrganizationAccountTestId', + GCP_SINGLE_ACCOUNT_TEST_ID: 'gcpSingleAccountTestId', + GCP_CLOUD_SHELL_TEST_ID: 'gcpGoogleCloudShellOptionTestId', + GCP_MANUAL_TEST_ID: 'gcpManualOptionTestId', + PRJ_ID_TEST_ID: 'project_id_test_id', + ORG_ID_TEST_ID: 'organization_id_test_id', + CREDENTIALS_TYPE_TEST_ID: 'credentials_type_test_id', + CREDENTIALS_FILE_TEST_ID: 'credentials_file_test_id', + CREDENTIALS_JSON_TEST_ID: 'textAreaInput-credentials-json', + CIS_AZURE_OPTION_TEST_ID: 'cisAzureTestId', + CIS_AZURE_SINGLE_SUB_TEST_ID: 'azureSingleAccountTestId', + AZURE_CREDENTIAL_SELECTOR: 'azure-credentials-type-selector', + CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS: { + TENANT_ID: 'cisAzureTenantId', + CLIENT_ID: 'cisAzureClientId', + CLIENT_SECRET: 'passwordInput-client-secret', + CLIENT_CERTIFICATE_PATH: 'cisAzureClientCertificatePath', + CLIENT_CERTIFICATE_PASSWORD: 'passwordInput-client-certificate-password', + CLIENT_USERNAME: 'cisAzureClientUsername', + CLIENT_PASSWORD: 'cisAzureClientPassword', + }, + CIS_AZURE_SETUP_FORMAT_TEST_SUBJECTS: { + ARM_TEMPLATE: 'cisAzureArmTemplate', + MANUAL: 'cisAzureManual', + }, + EVENTS_TABLE_ROW_CSS_SELECTOR: '[data-test-subj="events-viewer-panel"] .euiDataGridRow', + VISUALIZATIONS_SECTION_HEADER_TEST_ID: 'securitySolutionFlyoutVisualizationsHeader', + GRAPH_PREVIEW_CONTENT_TEST_ID: 'securitySolutionFlyoutGraphPreviewContent', + GRAPH_PREVIEW_LOADING_TEST_ID: 'securitySolutionFlyoutGraphPreviewLoading', + GRAPH_PREVIEW_TITLE_LINK_TEST_ID: 'securitySolutionFlyoutGraphPreviewTitleLink', + NODE_EXPAND_BUTTON_TEST_ID: 'cloudSecurityGraphNodeExpandButton', + GRAPH_INVESTIGATION_TEST_ID: 'cloudSecurityGraphGraphInvestigation', + GRAPH_NODE_EXPAND_POPOVER_TEST_ID: 'cloudSecurityGraphGraphInvestigationGraphNodeExpandPopover', + GRAPH_NODE_POPOVER_EXPLORE_RELATED_TEST_ID: + 'cloudSecurityGraphGraphInvestigationExploreRelatedEntities', + GRAPH_NODE_POPOVER_SHOW_ACTIONS_BY_TEST_ID: + 'cloudSecurityGraphGraphInvestigationShowActionsByEntity', + GRAPH_NODE_POPOVER_SHOW_ACTIONS_ON_TEST_ID: + 'cloudSecurityGraphGraphInvestigationShowActionsOnEntity', + GRAPH_LABEL_EXPAND_POPOVER_TEST_ID: 'cloudSecurityGraphGraphInvestigationGraphLabelExpandPopover', + GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID: + 'cloudSecurityGraphGraphInvestigationShowEventsWithThisAction', + GRAPH_ACTIONS_TOGGLE_SEARCH_ID: 'cloudSecurityGraphGraphInvestigationToggleSearch', + GRAPH_ACTIONS_INVESTIGATE_IN_TIMELINE_ID: + 'cloudSecurityGraphGraphInvestigationInvestigateInTimeline', + ALERT_TABLE_ROW_CSS_SELECTOR: '[data-test-subj="alertsTable"] .euiDataGridRow', + SETUP_TECHNOLOGY_SELECTOR: 'setup-technology-selector', + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ: 'setup-technology-selector-accordion', + SETUP_TECHNOLOGY_SELECTOR_AGENTLESS_OPTION: 'setup-technology-agentless-option', + DIRECT_ACCESS_KEYS: 'direct_access_keys', + SETUP_TECHNOLOGY_SELECTOR_AGENTLESS_RADIO: 'setup-technology-agentless-radio', + SETUP_TECHNOLOGY_SELECTOR_AGENT_BASED_RADIO: 'setup-technology-agent-based-radio', +}; diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts index 0bcb2a2ea2611..44a20dcbed81f 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/add_cis_integration_form_page.ts @@ -8,6 +8,7 @@ import { v4 as uuidv4 } from 'uuid'; import { setTimeout as sleep } from 'node:timers/promises'; import expect from '@kbn/expect'; +import { testSubjectIds } from '../constants/test_subject_ids'; import type { FtrProviderContext } from '../ftr_provider_context'; export function AddCisIntegrationFormPageProvider({ @@ -17,25 +18,10 @@ export function AddCisIntegrationFormPageProvider({ const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common', 'header']); const browser = getService('browser'); + const logger = getService('log'); const AWS_CREDENTIAL_SELECTOR = 'aws-credentials-type-selector'; - const testSubjectIds = { - AWS_SINGLE_ACCOUNT_TEST_ID: 'awsSingleTestId', - CIS_AWS_OPTION_TEST_ID: 'cisAwsTestId', - AWS_CREDENTIAL_SELECTOR: 'aws-credentials-type-selector', - SETUP_TECHNOLOGY_SELECTOR: 'setup-technology-selector', - SETUP_TECHNOLOGY_SELECTOR_AGENTLESS_RADIO: 'setup-technology-agentless-radio', - SETUP_TECHNOLOGY_SELECTOR_AGENT_BASED_RADIO: 'setup-technology-agent-based-radio', - DIRECT_ACCESS_KEYS: 'direct_access_keys', - DIRECT_ACCESS_KEY_ID_TEST_ID: 'awsDirectAccessKeyId', - DIRECT_ACCESS_SECRET_KEY_TEST_ID: 'passwordInput-secret-access-key', - PRJ_ID_TEST_ID: 'project_id_test_id', - CIS_GCP_OPTION_TEST_ID: 'cisGcpTestId', - GCP_SINGLE_ACCOUNT_TEST_ID: 'gcpSingleAccountTestId', - CREDENTIALS_JSON_TEST_ID: 'textAreaInput-credentials-json', - }; - const cisAzure = { getPostInstallArmTemplateModal: async () => { return await testSubjects.find('postInstallAzureArmTemplateModal'); @@ -525,6 +511,31 @@ export function AddCisIntegrationFormPageProvider({ return await agentName.getAttribute('value'); }; + const closeAllOpenTabs = async () => { + const handles = await browser.getAllWindowHandles(); + logger.debug(`Found ${handles.length} tabs to clean up`); + try { + // Keep the first tab and close all others in reverse order + for (let i = handles.length - 1; i > 0; i--) { + await browser.switchTab(i); + await browser.closeCurrentWindow(); + logger.debug(`Closed tab ${i}`); + } + + // Switch back to the first tab + await browser.switchTab(0); + logger.debug('Successfully closed all extra tabs and returned to main tab'); + } catch (err) { + logger.error(`Error while closing tabs: ${err}`); + // Attempt to return to first tab even if there was an error + try { + await browser.switchTab(0); + } catch (switchErr) { + logger.error(`Error switching back to first tab: ${switchErr}`); + } + } + }; + return { cisAzure, cisAws, @@ -579,5 +590,6 @@ export function AddCisIntegrationFormPageProvider({ showSetupTechnologyComponent, navigateToEditIntegrationPage, navigateToEditAgentlessIntegrationPage, + closeAllOpenTabs, }; } diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts index 6ebd496fca365..915d8ab9b3584 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/alerts_page.ts @@ -7,11 +7,14 @@ import expect from '@kbn/expect'; import { FtrService } from '../../functional/ftr_provider_context'; - -const ALERT_TABLE_ROW_CSS_SELECTOR = '[data-test-subj="alertsTable"] .euiDataGridRow'; -const VISUALIZATIONS_SECTION_HEADER_TEST_ID = 'securitySolutionFlyoutVisualizationsHeader'; -const GRAPH_PREVIEW_CONTENT_TEST_ID = 'securitySolutionFlyoutGraphPreviewContent'; -const GRAPH_PREVIEW_LOADING_TEST_ID = 'securitySolutionFlyoutGraphPreviewLoading'; +import { testSubjectIds } from '../constants/test_subject_ids'; + +const { + ALERT_TABLE_ROW_CSS_SELECTOR, + VISUALIZATIONS_SECTION_HEADER_TEST_ID, + GRAPH_PREVIEW_CONTENT_TEST_ID, + GRAPH_PREVIEW_LOADING_TEST_ID, +} = testSubjectIds; export class AlertsPageObject extends FtrService { private readonly retry = this.ctx.getService('retry'); diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout_graph.ts b/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout_graph.ts index 76bec567d483b..0ea78db43b0cf 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout_graph.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/expanded_flyout_graph.ts @@ -11,18 +11,22 @@ import type { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services'; import type { FilterBarService } from '@kbn/test-suites-src/functional/services/filter_bar'; import type { QueryBarProvider } from '../services/query_bar_provider'; import type { SecurityTelemetryFtrProviderContext } from '../config'; +import { testSubjectIds } from '../constants/test_subject_ids'; + +const { + GRAPH_PREVIEW_TITLE_LINK_TEST_ID, + NODE_EXPAND_BUTTON_TEST_ID, + GRAPH_INVESTIGATION_TEST_ID, + GRAPH_NODE_EXPAND_POPOVER_TEST_ID, + GRAPH_NODE_POPOVER_EXPLORE_RELATED_TEST_ID, + GRAPH_NODE_POPOVER_SHOW_ACTIONS_BY_TEST_ID, + GRAPH_NODE_POPOVER_SHOW_ACTIONS_ON_TEST_ID, + GRAPH_LABEL_EXPAND_POPOVER_TEST_ID, + GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID, + GRAPH_ACTIONS_TOGGLE_SEARCH_ID, + GRAPH_ACTIONS_INVESTIGATE_IN_TIMELINE_ID, +} = testSubjectIds; -const GRAPH_PREVIEW_TITLE_LINK_TEST_ID = 'securitySolutionFlyoutGraphPreviewTitleLink'; -const NODE_EXPAND_BUTTON_TEST_ID = 'cloudSecurityGraphNodeExpandButton'; -const GRAPH_INVESTIGATION_TEST_ID = 'cloudSecurityGraphGraphInvestigation'; -const GRAPH_NODE_EXPAND_POPOVER_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}GraphNodeExpandPopover`; -const GRAPH_NODE_POPOVER_EXPLORE_RELATED_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}ExploreRelatedEntities`; -const GRAPH_NODE_POPOVER_SHOW_ACTIONS_BY_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowActionsByEntity`; -const GRAPH_NODE_POPOVER_SHOW_ACTIONS_ON_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowActionsOnEntity`; -const GRAPH_LABEL_EXPAND_POPOVER_TEST_ID = `${GRAPH_INVESTIGATION_TEST_ID}GraphLabelExpandPopover`; -const GRAPH_LABEL_EXPAND_POPOVER_SHOW_EVENTS_WITH_THIS_ACTION_ITEM_ID = `${GRAPH_INVESTIGATION_TEST_ID}ShowEventsWithThisAction`; -const GRAPH_ACTIONS_TOGGLE_SEARCH_ID = `${GRAPH_INVESTIGATION_TEST_ID}ToggleSearch`; -const GRAPH_ACTIONS_INVESTIGATE_IN_TIMELINE_ID = `${GRAPH_INVESTIGATION_TEST_ID}InvestigateInTimeline`; type Filter = Parameters[0]; export class ExpandedFlyoutGraph extends GenericFtrService { diff --git a/x-pack/test/cloud_security_posture_functional/page_objects/network_events_page.ts b/x-pack/test/cloud_security_posture_functional/page_objects/network_events_page.ts index 8e03fae7eb7e0..dfddbc9717299 100644 --- a/x-pack/test/cloud_security_posture_functional/page_objects/network_events_page.ts +++ b/x-pack/test/cloud_security_posture_functional/page_objects/network_events_page.ts @@ -7,11 +7,14 @@ import expect from '@kbn/expect'; import { FtrService } from '../../functional/ftr_provider_context'; - -const EVENTS_TABLE_ROW_CSS_SELECTOR = '[data-test-subj="events-viewer-panel"] .euiDataGridRow'; -const VISUALIZATIONS_SECTION_HEADER_TEST_ID = 'securitySolutionFlyoutVisualizationsHeader'; -const GRAPH_PREVIEW_CONTENT_TEST_ID = 'securitySolutionFlyoutGraphPreviewContent'; -const GRAPH_PREVIEW_LOADING_TEST_ID = 'securitySolutionFlyoutGraphPreviewLoading'; +import { testSubjectIds } from '../constants/test_subject_ids'; + +const { + EVENTS_TABLE_ROW_CSS_SELECTOR, + VISUALIZATIONS_SECTION_HEADER_TEST_ID, + GRAPH_PREVIEW_CONTENT_TEST_ID, + GRAPH_PREVIEW_LOADING_TEST_ID, +} = testSubjectIds; export class NetworkEventsPageObject extends FtrService { private readonly retry = this.ctx.getService('retry'); diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts index 1072b8de18ac7..5ac6df898b46a 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cnvm/cis_integration_cnvm.ts @@ -10,12 +10,17 @@ import type { FtrProviderContext } from '../../../ftr_provider_context'; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { - const { getPageObjects } = providerContext; + const { getPageObjects, getService } = providerContext; const pageObjects = getPageObjects(['cloudPostureDashboard', 'cisAddIntegration', 'header']); describe('Test adding Cloud Security Posture Integrations CNVM', function () { this.tags(['cloud_security_posture_cis_integration_cnvm']); let cisIntegration: typeof pageObjects.cisAddIntegration; + const retry = getService('retry'); + const logger = getService('log'); + const RETRY_COUNT = 5; + const RETRY_DELAY = 1000; + const retryOptions = { retryCount: RETRY_COUNT, retryDelay: RETRY_DELAY }; beforeEach(async () => { cisIntegration = pageObjects.cisAddIntegration; @@ -25,14 +30,26 @@ export default function (providerContext: FtrProviderContext) { describe('CNVM AWS', () => { it('Hyperlink on PostInstallation Modal should have the correct URL', async () => { - await cisIntegration.navigateToAddIntegrationCnvmPage(); - await cisIntegration.inputUniqueIntegrationName(); - await pageObjects.header.waitUntilLoadingHasFinished(); - await cisIntegration.clickSaveButton(); - await pageObjects.header.waitUntilLoadingHasFinished(); - expect( - (await cisIntegration.getUrlOnPostInstallModal()) === - 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html' + await retry.tryWithRetries( + 'waiting for loading indicator to be hidden', + async () => { + await cisIntegration.navigateToAddIntegrationCnvmPage(); + await cisIntegration.inputUniqueIntegrationName(); + await pageObjects.header.waitUntilLoadingHasFinished(); + await cisIntegration.clickSaveButton(); + await pageObjects.header.waitUntilLoadingHasFinished(); + expect( + (await cisIntegration.getUrlOnPostInstallModal()) === + 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-whatis-howdoesitwork.html' + ); + return true; + }, + retryOptions, + async () => { + // Log the error or handle it in some way + logger.debug('Failed while waiting for loading indicator'); + return true; + } ); }); @@ -48,21 +65,6 @@ export default function (providerContext: FtrProviderContext) { )?.includes('https://console.aws.amazon.com/cloudformation/') ).to.be(true); }); - - it.skip('Clicking on Launch CloudFormation on post intall modal should lead user to Cloud Formation page', async () => { - await cisIntegration.navigateToAddIntegrationCnvmPage(); - await cisIntegration.inputUniqueIntegrationName(); - await pageObjects.header.waitUntilLoadingHasFinished(); - await cisIntegration.clickSaveButton(); - await pageObjects.header.waitUntilLoadingHasFinished(); - expect( - ( - await cisIntegration.clickLaunchAndGetCurrentUrl( - 'confirmCloudFormationModalConfirmButton' - ) - ).includes('console.aws.amazon.com%2Fcloudformation') - ).to.be(true); - }); }); }); } diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts index 451d539ae7b84..35d35311c6669 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_aws.ts @@ -7,24 +7,29 @@ import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { testSubjectIds } from '../../../constants/test_subject_ids'; -const CIS_AWS_OPTION_TEST_ID = 'cisAwsTestId'; -const AWS_SINGLE_ACCOUNT_TEST_ID = 'awsSingleTestId'; -const AWS_MANUAL_TEST_ID = 'aws-manual-setup-option'; -const AWS_CREDENTIAL_SELECTOR = 'aws-credentials-type-selector'; -const ROLE_ARN_TEST_ID = 'awsRoleArnInput'; -const DIRECT_ACCESS_KEY_ID_TEST_ID = 'awsDirectAccessKeyId'; -const DIRECT_ACCESS_SECRET_KEY_TEST_ID = 'passwordInput-secret-access-key'; -const TEMP_ACCESS_KEY_ID_TEST_ID = 'awsTemporaryKeysAccessKeyId'; -const TEMP_ACCESS_KEY_SECRET_KEY_TEST_ID = 'passwordInput-secret-access-key'; -const TEMP_ACCESS_SESSION_TOKEN_TEST_ID = 'awsTemporaryKeysSessionToken'; -const SHARED_CREDENTIALS_FILE_TEST_ID = 'awsSharedCredentialFile'; -const SHARED_CREDETIALS_PROFILE_NAME_TEST_ID = 'awsCredentialProfileName'; +const { + CIS_AWS_OPTION_TEST_ID, + AWS_SINGLE_ACCOUNT_TEST_ID, + AWS_MANUAL_TEST_ID, + AWS_CREDENTIAL_SELECTOR, + ROLE_ARN_TEST_ID, + DIRECT_ACCESS_KEY_ID_TEST_ID, + DIRECT_ACCESS_SECRET_KEY_TEST_ID, + TEMP_ACCESS_KEY_ID_TEST_ID, + TEMP_ACCESS_KEY_SECRET_KEY_TEST_ID, + TEMP_ACCESS_SESSION_TOKEN_TEST_ID, + SHARED_CREDENTIALS_FILE_TEST_ID, + SHARED_CREDETIALS_PROFILE_NAME_TEST_ID, +} = testSubjectIds; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { - const { getPageObjects } = providerContext; + const { getPageObjects, getService } = providerContext; const pageObjects = getPageObjects(['cloudPostureDashboard', 'cisAddIntegration', 'header']); + const retry = getService('retry'); + const logger = getService('log'); describe('Test adding Cloud Security Posture Integrations CSPM AWS', function () { this.tags(['cloud_security_posture_cis_integration_cspm_aws']); @@ -34,7 +39,7 @@ export default function (providerContext: FtrProviderContext) { beforeEach(async () => { cisIntegration = pageObjects.cisAddIntegration; cisIntegrationAws = pageObjects.cisAddIntegration.cisAws; - + await cisIntegration.closeAllOpenTabs(); await cisIntegration.navigateToAddIntegrationCspmPage(); }); @@ -69,22 +74,10 @@ export default function (providerContext: FtrProviderContext) { )?.includes('https://console.aws.amazon.com/cloudformation/') ).to.be(true); }); - it('Clicking on Launch CloudFormation on post intall modal should lead user to Cloud Formation page', async () => { - await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); - await cisIntegration.clickSaveButton(); - await pageObjects.header.waitUntilLoadingHasFinished(); - expect( - ( - await cisIntegration.clickLaunchAndGetCurrentUrl( - 'confirmCloudFormationModalConfirmButton' - ) - ).includes('console.aws.amazon.com%2Fcloudformation') - ).to.be(true); - }); }); // FLAKY: https://github.com/elastic/kibana/issues/187470 - describe.skip('CIS_AWS Organization Manual Assume Role', () => { + describe('CIS_AWS Organization Manual Assume Role', () => { it('CIS_AWS Organization Manual Assume Role Workflow', async () => { const roleArn = 'RoleArnTestValue'; await cisIntegration.clickOptionButton(CIS_AWS_OPTION_TEST_ID); @@ -92,7 +85,18 @@ export default function (providerContext: FtrProviderContext) { await cisIntegration.fillInTextField(ROLE_ARN_TEST_ID, roleArn); await cisIntegration.clickSaveButton(); await pageObjects.header.waitUntilLoadingHasFinished(); - expect((await cisIntegration.getPostInstallModal()) !== undefined).to.be(true); + + /* + * sometimes it takes a while to save the integration so added timeout to wait for post install modal + */ + await retry.try(async () => { + const modal = await cisIntegration.getPostInstallModal(); + if (!modal) { + logger.debug('Post install modal not found'); + } + expect(modal !== undefined).to.be(true); + }); + await cisIntegration.navigateToIntegrationCspList(); expect((await cisIntegration.getFieldValueInEditPage(ROLE_ARN_TEST_ID)) === roleArn).to.be( true diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts index d40276a33162b..1fe76970ae5d6 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_azure.ts @@ -7,10 +7,15 @@ import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { testSubjectIds } from '../../../constants/test_subject_ids'; -const CIS_AZURE_OPTION_TEST_ID = 'cisAzureTestId'; -const CIS_AZURE_SINGLE_SUB_TEST_ID = 'azureSingleAccountTestId'; -const AZURE_CREDENTIAL_SELECTOR = 'azure-credentials-type-selector'; +const { + CIS_AZURE_OPTION_TEST_ID, + CIS_AZURE_SINGLE_SUB_TEST_ID, + AZURE_CREDENTIAL_SELECTOR, + CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS, + CIS_AZURE_SETUP_FORMAT_TEST_SUBJECTS, +} = testSubjectIds; const clientId = 'clientIdTest'; const tenantId = 'tenantIdTest'; @@ -18,20 +23,6 @@ const clientCertificatePath = 'clientCertificatePathTest'; const clientSecret = 'clientSecretTest'; const clientCertificatePassword = 'clientCertificatePasswordTest'; -export const CIS_AZURE_INPUT_FIELDS_TEST_SUBJECTS = { - TENANT_ID: 'cisAzureTenantId', - CLIENT_ID: 'cisAzureClientId', - CLIENT_SECRET: 'passwordInput-client-secret', - CLIENT_CERTIFICATE_PATH: 'cisAzureClientCertificatePath', - CLIENT_CERTIFICATE_PASSWORD: 'passwordInput-client-certificate-password', - CLIENT_USERNAME: 'cisAzureClientUsername', - CLIENT_PASSWORD: 'cisAzureClientPassword', -}; -export const CIS_AZURE_SETUP_FORMAT_TEST_SUBJECTS = { - ARM_TEMPLATE: 'cisAzureArmTemplate', - MANUAL: 'cisAzureManual', -}; - // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { const { getPageObjects } = providerContext; diff --git a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts index 698c2db91938e..7f6f4e1cebfd0 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/cis_integrations/cspm/cis_integration_gcp.ts @@ -7,17 +7,20 @@ import expect from '@kbn/expect'; import type { FtrProviderContext } from '../../../ftr_provider_context'; +import { testSubjectIds } from '../../../constants/test_subject_ids'; -const CIS_GCP_OPTION_TEST_ID = 'cisGcpTestId'; -const GCP_ORGANIZATION_TEST_ID = 'gcpOrganizationAccountTestId'; -const GCP_SINGLE_ACCOUNT_TEST_ID = 'gcpSingleAccountTestId'; -const GCP_CLOUD_SHELL_TEST_ID = 'gcpGoogleCloudShellOptionTestId'; -const GCP_MANUAL_TEST_ID = 'gcpManualOptionTestId'; -const PRJ_ID_TEST_ID = 'project_id_test_id'; -const ORG_ID_TEST_ID = 'organization_id_test_id'; -const CREDENTIALS_TYPE_TEST_ID = 'credentials_type_test_id'; -const CREDENTIALS_FILE_TEST_ID = 'credentials_file_test_id'; -const CREDENTIALS_JSON_TEST_ID = 'textAreaInput-credentials-json'; +const { + CIS_GCP_OPTION_TEST_ID, + GCP_ORGANIZATION_TEST_ID, + GCP_SINGLE_ACCOUNT_TEST_ID, + GCP_CLOUD_SHELL_TEST_ID, + GCP_MANUAL_TEST_ID, + PRJ_ID_TEST_ID, + ORG_ID_TEST_ID, + CREDENTIALS_TYPE_TEST_ID, + CREDENTIALS_FILE_TEST_ID, + CREDENTIALS_JSON_TEST_ID, +} = testSubjectIds; // eslint-disable-next-line import/no-default-export export default function (providerContext: FtrProviderContext) { diff --git a/x-pack/test/cloud_security_posture_functional/pages/findings_onboarding.ts b/x-pack/test/cloud_security_posture_functional/pages/findings_onboarding.ts index cfd044eea9501..3bd88a6803b6b 100644 --- a/x-pack/test/cloud_security_posture_functional/pages/findings_onboarding.ts +++ b/x-pack/test/cloud_security_posture_functional/pages/findings_onboarding.ts @@ -15,47 +15,18 @@ export default ({ getPageObjects }: FtrProviderContext) => { describe('Findings Page onboarding', function () { this.tags(['cloud_security_posture_findings_onboarding']); let findings: typeof PageObjects.findings; - let notInstalledVulnerabilities: typeof findings.notInstalledVulnerabilities; let notInstalledCSP: typeof findings.notInstalledCSP; let thirdPartyIntegrationsNoMisconfigurationsFindingsPrompt: typeof findings.thirdPartyIntegrationsNoMisconfigurationsFindingsPrompt; - let thirdPartyIntegrationsNoVulnerabilitiesFindingsPrompt: typeof findings.thirdPartyIntegrationsNoVulnerabilitiesFindingsPrompt; beforeEach(async () => { findings = PageObjects.findings; - notInstalledVulnerabilities = findings.notInstalledVulnerabilities; notInstalledCSP = findings.notInstalledCSP; thirdPartyIntegrationsNoMisconfigurationsFindingsPrompt = findings.thirdPartyIntegrationsNoMisconfigurationsFindingsPrompt; - thirdPartyIntegrationsNoVulnerabilitiesFindingsPrompt = - findings.thirdPartyIntegrationsNoVulnerabilitiesFindingsPrompt; await findings.waitForPluginInitialized(); }); - it('Vulnerabilities - clicking on the `No integrations installed` prompt action button - `install CNVM`: navigates to the CNVM integration installation page', async () => { - await findings.navigateToLatestVulnerabilitiesPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const element = await notInstalledVulnerabilities.getElement(); - expect(element).to.not.be(null); - - await notInstalledVulnerabilities.navigateToAction('cnvm-not-installed-action'); - - await PageObjects.common.waitUntilUrlIncludes('add-integration/vuln_mgmt'); - }); - - it('Vulnerabilities - clicking on the `Third party integrations` prompt action button - `Wiz Integration`: navigates to the Wiz integration installation page', async () => { - await findings.navigateToLatestVulnerabilitiesPage(); - await PageObjects.header.waitUntilLoadingHasFinished(); - const element = await thirdPartyIntegrationsNoVulnerabilitiesFindingsPrompt.getElement(); - expect(element).to.not.be(null); - - await thirdPartyIntegrationsNoVulnerabilitiesFindingsPrompt.navigateToAction( - '3p-no-vulnerabilities-findings-prompt-wiz-integration-button' - ); - - await PageObjects.common.waitUntilUrlIncludes('fleet/integrations/wiz/add-integration'); - }); - it('Misconfigurations - clicking on the `No integrations installed` prompt action button - `install cloud posture integration`: navigates to the CSPM integration installation page', async () => { await findings.navigateToMisconfigurations(); await PageObjects.header.waitUntilLoadingHasFinished();