diff --git a/.buildkite/pipelines/pull_request/scout_ui_tests.yml b/.buildkite/pipelines/pull_request/scout_ui_tests.yml index 37ea1567c4f42..16981921f79a9 100644 --- a/.buildkite/pipelines/pull_request/scout_ui_tests.yml +++ b/.buildkite/pipelines/pull_request/scout_ui_tests.yml @@ -2,7 +2,7 @@ steps: - command: .buildkite/scripts/steps/functional/scout_ui_tests.sh label: 'Scout UI Tests' agents: - machineType: n2-standard-4 + machineType: n2-standard-8 preemptible: true depends_on: - build @@ -11,7 +11,7 @@ steps: - linting - linting_with_types - check_types - timeout_in_minutes: 30 + timeout_in_minutes: 60 retry: automatic: - exit_status: '-1' diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 4353f4f079a9f..fd8ebb042ed46 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -109,18 +109,6 @@ const getPipeline = (filename: string, removeSteps = true) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/inventory_cypress.yml')); } - if ( - (await doAnyChangesMatch([ - /^x-pack\/solutions\/observability\/plugins\/observability_onboarding/, - /^x-pack\/platform\/plugins\/shared\/fleet/, - ])) || - GITHUB_PR_LABELS.includes('ci:all-cypress-suites') - ) { - pipeline.push( - getPipeline('.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml') - ); - } - if ( (await doAnyChangesMatch([/^x-pack\/solutions\/observability\/plugins\/profiling/])) || GITHUB_PR_LABELS.includes('ci:all-cypress-suites') @@ -405,6 +393,7 @@ const getPipeline = (filename: string, removeSteps = true) => { if ( (await doAnyChangesMatch([ /^x-pack\/platform\/plugins\/private\/discover_enhanced\/ui_tests/, + /^x-pack\/solutions\/observability\/plugins\/observability_onboarding/, /^packages\/kbn-scout/, ])) || GITHUB_PR_LABELS.includes('ci:scout-ui-tests') diff --git a/.buildkite/scripts/steps/functional/scout_ui_tests.sh b/.buildkite/scripts/steps/functional/scout_ui_tests.sh index b568ed9c80b1a..c16c92152684d 100755 --- a/.buildkite/scripts/steps/functional/scout_ui_tests.sh +++ b/.buildkite/scripts/steps/functional/scout_ui_tests.sh @@ -6,29 +6,33 @@ source .buildkite/scripts/steps/functional/common.sh export JOB=kibana-scout-ui-tests -TEST_CONFIG="x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts" KIBANA_DIR="$KIBANA_BUILD_LOCATION" -declare -A TESTS=( - ["Stateful"]="--stateful" - ["Serverless Elasticsearch"]="--serverless=es" - ["Serverless Observability"]="--serverless=oblt" - ["Serverless Security"]="--serverless=security" -) +run_tests() { + local suit_name=$1 + local config_path=$2 + local run_mode=$3 -ORDER=("Stateful" "Serverless Elasticsearch" "Serverless Observability" "Serverless Security") - -EXIT_CODE=0 - -for TEST_NAME in "${ORDER[@]}"; do - RUN_MODE="${TESTS[$TEST_NAME]}" - echo "--- $TEST_NAME: 'discover_enhanced' plugin UI Tests" - if ! node scripts/scout run-tests "$RUN_MODE" --config "$TEST_CONFIG" --kibana-install-dir "$KIBANA_DIR"; then - echo "$TEST_NAME: failed" + echo "--- $suit_name ($run_mode) UI Tests" + if ! node scripts/scout run-tests "$run_mode" --config "$config_path" --kibana-install-dir "$KIBANA_DIR"; then + echo "$suit_name: failed" EXIT_CODE=1 else - echo "$TEST_NAME: passed" + echo "$suit_name: passed" fi +} + +EXIT_CODE=0 + +# Discovery Enhanced +for run_mode in "--stateful" "--serverless=es" "--serverless=oblt" "--serverless=security"; do + run_tests "Discovery Enhanced" "x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts" "$run_mode" +done + +# Observability Onboarding +for run_mode in "--stateful" "--serverless=oblt"; do + run_tests "Observability Onboarding" "x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts" "$run_mode" done + exit $EXIT_CODE diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f0383e9e556d6..c9af0fd8d5c1b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -932,7 +932,6 @@ x-pack/solutions/observability/plugins/observability_ai_assistant_app @elastic/o x-pack/solutions/observability/plugins/observability_ai_assistant_management @elastic/obs-ai-assistant x-pack/solutions/observability/plugins/observability_logs_explorer @elastic/obs-ux-logs-team x-pack/solutions/observability/plugins/observability_onboarding @elastic/obs-ux-logs-team -x-pack/solutions/observability/plugins/observability_onboarding/e2e @elastic/obs-ux-logs-team x-pack/solutions/observability/plugins/observability_shared @elastic/observability-ui x-pack/solutions/observability/plugins/observability_solution/entities_data_access @elastic/obs-entities x-pack/solutions/observability/plugins/observability_solution/entity_manager_app @elastic/obs-entities diff --git a/package.json b/package.json index 467428ad6404c..44730ac027fe9 100644 --- a/package.json +++ b/package.json @@ -1474,7 +1474,6 @@ "@kbn/manifest": "link:packages/kbn-manifest", "@kbn/mock-idp-plugin": "link:packages/kbn-mock-idp-plugin", "@kbn/mock-idp-utils": "link:packages/kbn-mock-idp-utils", - "@kbn/observability-onboarding-e2e": "link:x-pack/solutions/observability/plugins/observability_onboarding/e2e", "@kbn/openapi-bundler": "link:packages/kbn-openapi-bundler", "@kbn/openapi-generator": "link:packages/kbn-openapi-generator", "@kbn/optimizer": "link:packages/kbn-optimizer", diff --git a/tsconfig.base.json b/tsconfig.base.json index 97d73eaa868f5..2a439e72882bb 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1336,8 +1336,6 @@ "@kbn/observability-logs-explorer-plugin/*": ["x-pack/solutions/observability/plugins/observability_logs_explorer/*"], "@kbn/observability-logs-overview": ["x-pack/platform/packages/shared/observability/logs_overview"], "@kbn/observability-logs-overview/*": ["x-pack/platform/packages/shared/observability/logs_overview/*"], - "@kbn/observability-onboarding-e2e": ["x-pack/solutions/observability/plugins/observability_onboarding/e2e"], - "@kbn/observability-onboarding-e2e/*": ["x-pack/solutions/observability/plugins/observability_onboarding/e2e/*"], "@kbn/observability-onboarding-plugin": ["x-pack/solutions/observability/plugins/observability_onboarding"], "@kbn/observability-onboarding-plugin/*": ["x-pack/solutions/observability/plugins/observability_onboarding/*"], "@kbn/observability-plugin": ["x-pack/solutions/observability/plugins/observability"], diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/README.md b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/README.md deleted file mode 100644 index 2c35d48fb5207..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Observability onboarding E2E tests - -Observability onboarding uses [FTR](../../../../../packages/kbn-test/README.mdx) (functional test runner) and [Cypress](https://www.cypress.io/) to run the e2e tests. The tests are located at `kibana/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e`. - -## E2E Tests (Cypress) - -The E2E tests are located in [`x-pack/solutions/observability/plugins/observability_onboarding/e2e`](./cypress/e2e). - -Tests run on buildkite PR pipeline are parallelized (2 parallel jobs) and are orchestrated by the Cypress dashboard service. It can be configured in [.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml](https://github.com/elastic/kibana/blob/main/.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml) with the property `parallelism`. - -```yml - ... - depends_on: build - parallelism: 2 - ... -``` - -## Running it locally - -### Start test server - -``` -node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e --server -``` - -### Run tests -Runs all tests in the terminal - -``` -node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e --runner -``` - -### Open cypress dashboard -Opens cypress dashboard, there it's possible to select what test you want to run. - -``` -node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e --open -``` -### Arguments - -| Option | Description | -| ------------ | ----------------------------------------------- | -| --server | Only start ES and Kibana | -| --runner | Only run tests | -| --spec | Specify the specs to run | -| --times | Repeat the test n number of times | -| --bail | stop tests after the first failure | - -``` -node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e.js --runner --spec cypress/e2e/home.cy.ts --times 2 -``` diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress.config.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress.config.ts deleted file mode 100644 index afbad0bac67d0..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress.config.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 { defineCypressConfig } from '@kbn/cypress-config'; - -export default defineCypressConfig({ - fileServerFolder: './cypress', - fixturesFolder: './cypress/fixtures', - screenshotsFolder: './cypress/screenshots', - videosFolder: './cypress/videos', - requestTimeout: 10000, - responseTimeout: 40000, - defaultCommandTimeout: 30000, - execTimeout: 120000, - pageLoadTimeout: 120000, - viewportHeight: 1800, - viewportWidth: 1440, - video: false, - screenshotOnRunFailure: false, - retries: { - runMode: 1, - }, - e2e: { - baseUrl: 'http://localhost:5601', - supportFile: './cypress/support/e2e.ts', - specPattern: './cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', - }, -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts deleted file mode 100644 index 27392c65e9dc7..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/home.cy.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -// Failing: See https://github.com/elastic/kibana/issues/183341 -describe.skip('[Observability onboarding] Landing page', () => { - beforeEach(() => { - cy.loginAsElastic(); - }); - - describe('Entry point', () => { - it('when clicking on the logs card the user is navigated to the observability onboarding page', () => { - cy.getByTestSubj('guideButtonRedirect').click(); - cy.getByTestSubj('guide-filter-observability').click(); - cy.getByTestSubj('onboarding--observability--logs').click(); - - cy.url().should('include', '/app/observabilityOnboarding'); - }); - - it('when clicking on observability overview callout the user is navigated to the observability onboarding page', () => { - cy.visitKibana('/app/observability'); - cy.getByTestSubj('observability-onboarding-callout').should('exist'); - cy.getByTestSubj('o11yObservabilityOnboardingGetStartedButton').click(); - - cy.url().should('include', '/app/observabilityOnboarding'); - }); - }); - - it('when user navigates to observability onboarding landing page is showed', () => { - cy.visitKibana('/app/observabilityOnboarding'); - cy.contains('Onboard Observability data'); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/configure.cy.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/configure.cy.ts deleted file mode 100644 index 3a4ceba04f706..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/configure.cy.ts +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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. - */ - -describe('[Logs onboarding] Custom logs - configure step', () => { - describe('logFilePaths', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - }); - - describe('when user clicks on back button', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - }); - - it('navigates to observability logs onboarding page', () => { - cy.getByTestSubj('observabilityOnboardingFlowBackToSelectionButton').click(); - - cy.url().should('include', '/app/observabilityOnboarding'); - }); - }); - - it('Users shouldnt be able to continue if logFilePaths is empty', () => { - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').should('not.have.text'); - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled'); - }); - - it('Users should be able to continue if logFilePaths is not empty', () => { - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log'); - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('not.be.disabled'); - }); - - it('Users can add multiple logFilePaths', () => { - cy.getByTestSubj('obltOnboardingCustomLogsAddFilePath').click(); - cy.getByTestSubj('obltOnboardingLogFilePath-0').should('exist'); - cy.getByTestSubj('obltOnboardingLogFilePath-1').should('exist'); - }); - - it('Users can delete logFilePaths', () => { - cy.getByTestSubj('obltOnboardingCustomLogsAddFilePath').click(); - cy.get('*[data-test-subj^="obltOnboardingLogFilePath-"]').should('have.length', 2); - - cy.getByTestSubj('obltOnboardingLogFilePathDelete-1').click(); - cy.get('*[data-test-subj^="obltOnboardingLogFilePath-"]').should('have.length', 1); - }); - - describe('when users fill logFilePaths', () => { - it('datasetname and integration name are auto generated if it is the first path', () => { - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log'); - cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').should('have.value', 'mylogs'); - cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').should('have.value', 'mylogs'); - }); - - it('datasetname and integration name are not generated if it is not the first path', () => { - cy.getByTestSubj('obltOnboardingCustomLogsAddFilePath').click(); - cy.getByTestSubj('obltOnboardingLogFilePath-1').find('input').type('myLogs.log'); - cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').should('be.empty'); - cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').should('be.empty'); - }); - }); - }); - - describe('serviceName', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log'); - }); - - it('should be optional allowing user to continue if it is empty', () => { - cy.getByTestSubj('obltOnboardingCustomLogsServiceName').should('not.have.text'); - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.enabled'); - }); - }); - - describe('advancedSettings', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log'); - }); - - it('Users should expand the content when clicking it', () => { - cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click(); - - cy.getByTestSubj('obltOnboardingCustomLogsNamespace').should('be.visible'); - cy.getByTestSubj('obltOnboardingCustomLogsCustomConfig').should('be.visible'); - }); - - it('Users should hide the content when clicking it', () => { - cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click(); - - cy.getByTestSubj('obltOnboardingCustomLogsNamespace').should('not.be.visible'); - cy.getByTestSubj('obltOnboardingCustomLogsCustomConfig').should('not.be.visible'); - }); - - describe('Namespace', () => { - beforeEach(() => { - cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click(); - }); - - afterEach(() => { - cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click(); - }); - - it('Users should see a default namespace', () => { - cy.getByTestSubj('obltOnboardingCustomLogsNamespace').should('have.value', 'default'); - }); - - it('Users should not be able to continue if they do not specify a namespace', () => { - cy.getByTestSubj('obltOnboardingCustomLogsNamespace').clear(); - - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled'); - }); - }); - - describe('customConfig', () => { - beforeEach(() => { - cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click(); - }); - - afterEach(() => { - cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click(); - }); - - it('should be optional allowing user to continue if it is empty', () => { - cy.getByTestSubj('obltOnboardingCustomLogsCustomConfig').should('not.have.text'); - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.enabled'); - }); - }); - }); - - describe('integrationName', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log'); - }); - - it('Users should not be able to continue if they do not specify an integrationName', () => { - cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').clear(); - - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled'); - }); - - it('value will contain _ instead of special chars', () => { - cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').clear().type('hello$world'); - - cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').should( - 'have.value', - 'hello_world' - ); - }); - - it('value will be invalid if it is not lowercase', () => { - cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').clear().type('H3llowOrld'); - - cy.contains('An integration name should be lowercase.'); - }); - }); - - describe('datasetName', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log'); - }); - - it('Users should not be able to continue if they do not specify a datasetName', () => { - cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').clear(); - - cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled'); - }); - - it('value will contain _ instead of special chars', () => { - cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').clear().type('hello$world'); - - cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').should('have.value', 'hello_world'); - }); - - it('value will be invalid if it is not lowercase', () => { - cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').clear().type('H3llowOrld'); - - cy.contains('A dataset name should be lowercase.'); - }); - }); - - describe('custom integration', () => { - const CUSTOM_INTEGRATION_NAME = 'mylogs'; - - beforeEach(() => { - cy.deleteIntegration(CUSTOM_INTEGRATION_NAME); - }); - - describe('when user is missing privileges', () => { - beforeEach(() => { - cy.loginAsViewerUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0') - .find('input') - .type(`${CUSTOM_INTEGRATION_NAME}.log`); - - cy.getByTestSubj('obltOnboardingCustomLogsContinue').click(); - }); - - it('installation fails', () => { - cy.getByTestSubj('obltOnboardingCustomIntegrationErrorCallout').should('exist'); - }); - }); - - describe('when user has proper privileges', () => { - beforeEach(() => { - cy.loginAsEditorUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0') - .find('input') - .type(`${CUSTOM_INTEGRATION_NAME}.log`); - - cy.getByTestSubj('obltOnboardingCustomLogsContinue').click(); - }); - - afterEach(() => { - cy.deleteIntegration(CUSTOM_INTEGRATION_NAME); - }); - - it('installation succeed and user is redirected install elastic agent step', () => { - cy.url().should('include', '/app/observabilityOnboarding/customLogs/installElasticAgent'); - }); - }); - - it('installation fails if integration already exists', () => { - cy.loginAsEditorUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.installCustomIntegration(CUSTOM_INTEGRATION_NAME); - cy.getByTestSubj('obltOnboardingLogFilePath-0') - .find('input') - .type(`${CUSTOM_INTEGRATION_NAME}.log`); - cy.getByTestSubj('obltOnboardingCustomLogsContinue').click(); - - cy.contains( - 'Failed to create the integration as an installation with the name mylogs already exists.' - ); - }); - - describe('when an error occurred on creation', () => { - before(() => { - cy.intercept('/api/fleet/epm/custom_integrations', { - statusCode: 500, - body: { - message: 'Internal error', - }, - }); - - cy.loginAsEditorUser(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.getByTestSubj('obltOnboardingLogFilePath-0') - .find('input') - .type(`${CUSTOM_INTEGRATION_NAME}.log`); - cy.getByTestSubj('obltOnboardingCustomLogsContinue').click(); - }); - - it('user should see the error displayed', () => { - cy.getByTestSubj('obltOnboardingCustomIntegrationErrorCallout').should('exist'); - }); - }); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts deleted file mode 100644 index 3b9248dc3c9e0..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/custom_logs/install_elastic_agent.cy.ts +++ /dev/null @@ -1,494 +0,0 @@ -/* - * 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. - */ - -describe('[Logs onboarding] Custom logs - install elastic agent', () => { - const CUSTOM_INTEGRATION_NAME = 'mylogs'; - - const configureCustomLogs = (loginFn = () => cy.loginAsLogMonitoringUser()) => { - loginFn(); - cy.visitKibana('/app/observabilityOnboarding/customLogs'); - - cy.deleteIntegration(CUSTOM_INTEGRATION_NAME); - - cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('mylogs.log'); - - cy.getByTestSubj('obltOnboardingCustomLogsContinue').click(); - }; - - describe('custom integration', () => { - beforeEach(() => { - configureCustomLogs(() => cy.loginAsEditorUser()); - }); - - it('Users should be able to see the custom integration success callout', () => { - cy.getByTestSubj('obltOnboardingCustomIntegrationInstalled').should('be.visible'); - }); - }); - - describe('ApiKey generation', () => { - describe('when user is missing privileges', () => { - beforeEach(() => { - configureCustomLogs(() => cy.loginAsEditorUser()); - }); - - it('apiKey is not generated', () => { - cy.getByTestSubj('obltOnboardingLogsApiKeyCreationNoPrivileges').should('exist'); - }); - }); - - describe('when user has proper privileges', () => { - beforeEach(() => { - configureCustomLogs(); - }); - - it('apiKey is generated', () => { - cy.getByTestSubj('obltOnboardingLogsApiKeyCreated').should('exist'); - }); - }); - - describe('when an error occurred on creation', () => { - before(() => { - cy.intercept('/internal/observability_onboarding/logs/flow', { - statusCode: 500, - body: { - message: 'Internal error', - }, - }); - - configureCustomLogs(); - }); - - it('apiKey is not generated', () => { - cy.getByTestSubj('obltOnboardingLogsApiKeyCreationFailed').should('exist'); - }); - }); - }); - - describe('Install the Elastic Agent step', () => { - beforeEach(() => { - cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as( - 'createOnboardingFlow' - ); - configureCustomLogs(); - }); - - describe('When user select Linux OS', () => { - it('Auto download config to host is disabled by default', () => { - cy.get('.euiButtonGroup').contains('Linux').click(); - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig') - .should('be.enabled') - .should('not.be.checked'); - }); - - it('Installation script is shown', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentStep') - .get('.euiCodeBlock') - .should('exist'); - }); - }); - - describe('When user select Mac OS', () => { - beforeEach(() => { - cy.get('.euiButtonGroup').contains('MacOS').click(); - }); - - it('Auto download config to host is disabled by default', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig') - .should('be.enabled') - .should('not.be.checked'); - }); - - it('Installation script is shown', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentStep') - .get('.euiCodeBlock') - .should('exist'); - }); - }); - - describe('When user select Windows OS', () => { - beforeEach(() => { - cy.get('.euiButtonGroup').contains('Windows').click(); - }); - - it('Auto download config to host is disabled by default', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig') - .should('be.disabled') - .should('not.be.checked'); - }); - - it('A link to the documentation is shown instead of installation script', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentWindowsDocsLink').should('exist'); - - cy.getByTestSubj('obltOnboardingInstallElasticAgentStep') - .get('.euiCodeBlock') - .should('not.exist'); - }); - }); - - describe('When Auto download config', () => { - describe('is selected', () => { - it('autoDownloadConfig flag is added to installation script', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click(); - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfigCallout').should( - 'exist' - ); - cy.getByTestSubj('obltOnboardingInstallElasticAgentStep') - .get('.euiCodeBlock') - .should('contain', 'autoDownloadConfig=1'); - }); - - it('Download config button is disabled', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click(); - cy.getByTestSubj('obltOnboardingConfigureElasticAgentStepDownloadConfig').should( - 'be.disabled' - ); - }); - }); - - it('is not selected autoDownloadConfig flag is not added to installation script', () => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentStep') - .get('.euiCodeBlock') - .should('not.contain', 'autoDownloadConfig=1'); - }); - }); - - describe('When user executes the installation script in the host', () => { - let onboardingId: string; - - describe('updates on steps are shown in the flow', () => { - beforeEach(() => { - cy.wait('@createOnboardingFlow') - .its('response.body') - .then((body) => { - onboardingId = body.onboardingId; - }); - }); - - describe('Download elastic Agent step', () => { - it('shows a loading callout when elastic agent is downloading', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'loading'); - cy.getByTestSubj('obltOnboardingStepStatus-loading') - .contains('Downloading Elastic Agent') - .should('exist'); - }); - - it('shows a success callout when elastic agent is downloaded', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.getByTestSubj('obltOnboardingStepStatus-complete') - .contains('Elastic Agent downloaded') - .should('exist'); - }); - - it('shows a danger callout when elastic agent was not downloaded', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'danger'); - cy.getByTestSubj('obltOnboardingStepStatus-danger') - .contains('Download Elastic Agent') - .should('exist'); - }); - }); - - describe('Extract elastic Agent step', () => { - beforeEach(() => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - }); - - it('shows a loading callout when elastic agent is extracting', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'loading'); - cy.getByTestSubj('obltOnboardingStepStatus-loading') - .contains('Extracting Elastic Agent') - .should('exist'); - }); - - it('shows a success callout when elastic agent is extracted', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - cy.getByTestSubj('obltOnboardingStepStatus-complete') - .contains('Elastic Agent extracted') - .should('exist'); - }); - - it('shows a danger callout when elastic agent was not extracted', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'danger'); - cy.getByTestSubj('obltOnboardingStepStatus-danger') - .contains('Extract Elastic Agent') - .should('exist'); - }); - }); - - describe('Install elastic Agent step', () => { - beforeEach(() => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - }); - - it('shows a loading callout when elastic agent is installing', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'loading'); - cy.getByTestSubj('obltOnboardingStepStatus-loading') - .contains('Installing Elastic Agent') - .should('exist'); - }); - - it('shows a success callout when elastic agent is installed', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); - cy.getByTestSubj('obltOnboardingStepStatus-complete') - .contains('Elastic Agent installed') - .should('exist'); - }); - - it('shows a danger callout when elastic agent was not installed', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'danger'); - cy.getByTestSubj('obltOnboardingStepStatus-danger') - .contains('Install Elastic Agent') - .should('exist'); - }); - }); - - describe('Check elastic Agent status step', () => { - beforeEach(() => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); - }); - - it('shows a loading callout when getting elastic agent status', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading'); - cy.getByTestSubj('obltOnboardingStepStatus-loading') - .contains('Connecting to the Elastic Agent') - .should('exist'); - }); - - it('shows a success callout when elastic agent status is healthy', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { - agentId: 'test-agent-id', - }); - cy.getByTestSubj('obltOnboardingStepStatus-complete') - .contains('Connected to the Elastic Agent') - .should('exist'); - }); - - it('shows a warning callout when elastic agent status is not healthy', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'warning'); - cy.getByTestSubj('obltOnboardingStepStatus-warning') - .contains('Connect to the Elastic Agent') - .should('exist'); - }); - }); - }); - }); - }); - - describe('Configure Elastic Agent step', () => { - let onboardingId: string; - - beforeEach(() => { - cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as( - 'createOnboardingFlow' - ); - configureCustomLogs(); - cy.wait('@createOnboardingFlow') - .its('response.body') - .then((body) => { - onboardingId = body.onboardingId; - }); - }); - - describe('When user select Linux OS', () => { - beforeEach(() => { - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click(); - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { - agentId: 'test-agent-id', - }); - }); - - it('shows loading callout when config is being downloaded to the host', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading'); - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]' - ).should('exist'); - cy.getByTestSubj('obltOnboardingStepStatus-loading') - .contains('Downloading Elastic Agent config') - .should('exist'); - }); - - it('shows success callout when the configuration has been written to the host', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]' - ).should('exist'); - cy.getByTestSubj('obltOnboardingStepStatus-complete') - .contains('Elastic Agent config written to /opt/Elastic/Agent/elastic-agent.yml') - .should('exist'); - }); - - it('shows warning callout when the configuration was not written in the host', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning'); - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-warning"]' - ).should('exist'); - cy.getByTestSubj('obltOnboardingStepStatus-warning') - .contains('Configure the agent') - .should('exist'); - }); - }); - - describe('When user select Mac OS', () => { - beforeEach(() => { - cy.get('.euiButtonGroup').contains('MacOS').click(); - cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click(); - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { - agentId: 'test-agent-id', - }); - }); - - it('shows loading callout when config is being downloaded to the host', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading'); - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]' - ).should('exist'); - cy.getByTestSubj('obltOnboardingStepStatus-loading') - .contains('Downloading Elastic Agent config') - .should('exist'); - }); - - it('shows success callout when the configuration has been written to the host', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]' - ).should('exist'); - cy.getByTestSubj('obltOnboardingStepStatus-complete') - .contains('Elastic Agent config written to /Library/Elastic/Agent/elastic-agent.yml') - .should('exist'); - }); - - it('shows warning callout when the configuration was not written in the host', () => { - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning'); - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-warning"]' - ).should('exist'); - cy.getByTestSubj('obltOnboardingStepStatus-warning') - .contains('Configure the agent') - .should('exist'); - }); - }); - - describe('When user select Windows', () => { - beforeEach(() => { - cy.get('.euiButtonGroup').contains('Windows').click(); - }); - - it('step is disabled', () => { - cy.get( - '[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]' - ).should('exist'); - }); - }); - }); - - describe('Check logs step', () => { - let onboardingId: string; - - beforeEach(() => { - cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as( - 'createOnboardingFlow' - ); - configureCustomLogs(); - cy.wait('@createOnboardingFlow') - .its('response.body') - .then((body) => { - onboardingId = body.onboardingId; - }); - }); - - describe('When user select Linux OS or MacOS', () => { - describe('When configure Elastic Agent step is not finished', () => { - beforeEach(() => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading'); - }); - - it('check logs is not triggered', () => { - cy.get( - '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-incomplete"]' - ).should('exist'); - cy.get('.euiStep__title').contains('Ship logs to Elastic Observability').should('exist'); - }); - }); - - describe('When configure Elastic Agent step has finished', () => { - beforeEach(() => { - cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); - cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { - agentId: 'test-agent-id', - }); - cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); - }); - - it('shows loading callout when logs are being checked', () => { - cy.get( - '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]' - ).should('exist'); - cy.get('.euiStep__title').contains('Waiting for logs to be shipped...').should('exist'); - }); - }); - }); - - describe('When user select Windows', () => { - beforeEach(() => { - cy.get('.euiButtonGroup').contains('Windows').click(); - }); - - it('step is disabled', () => { - cy.get( - '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]' - ).should('exist'); - }); - }); - }); - - describe('When logs are being shipped', () => { - beforeEach(() => { - cy.intercept('GET', '**/progress', { - status: 200, - body: { - progress: { - 'ea-download': { status: 'complete' }, - 'ea-extract': { status: 'complete' }, - 'ea-install': { status: 'complete' }, - 'ea-status': { status: 'complete' }, - 'ea-config': { status: 'complete' }, - 'logs-ingest': { status: 'complete' }, - }, - }, - }).as('checkOnboardingProgress'); - configureCustomLogs(); - }); - - it('shows success callout when logs has arrived to elastic', () => { - cy.wait('@checkOnboardingProgress'); - cy.get( - '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]' - ).should('exist'); - cy.get('.euiStep__title').contains('Logs are being shipped!').should('exist'); - }); - - it('when user clicks on Explore Logs it navigates to observability logs explorer', () => { - cy.wait('@checkOnboardingProgress'); - cy.getByTestSubj('obltOnboardingExploreLogs').should('exist').click(); - - cy.url().should('include', '/app/discover'); - }); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/feedback.cy.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/feedback.cy.ts deleted file mode 100644 index 542ca6ac80222..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/logs/feedback.cy.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ - -// Failing: See https://github.com/elastic/kibana/issues/183341 -describe.skip('[Logs onboarding] Give Feedback', () => { - beforeEach(() => { - cy.loginAsElastic(); - cy.visitKibana('/app/observabilityOnboarding'); - }); - - it('feedback button is present in custom logs onboarding', () => { - cy.getByTestSubj('obltOnboardingHomeStartLogFileStream').click(); - cy.getByTestSubj('observabilityOnboardingPageGiveFeedback').should('exist'); - }); - - it('feedback button is not present in the landing page', () => { - cy.getByTestSubj('observabilityOnboardingPageGiveFeedback').should('not.exist'); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/navigation.cy.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/navigation.cy.ts deleted file mode 100644 index ebaa607179791..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e/navigation.cy.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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. - */ - -// Failing: See https://github.com/elastic/kibana/issues/183341 -describe.skip('[Observability onboarding] Navigation', () => { - beforeEach(() => { - cy.loginAsElastic(); - cy.visitKibana('/app/observabilityOnboarding/'); - }); - - describe('When user clicks on the card', () => { - it('navigates to custom logs onboarding', () => { - cy.getByTestSubj('obltOnboardingHomeStartLogFileStream').click(); - - cy.url().should('include', '/app/observabilityOnboarding/customLogs'); - }); - - it('navigates to apm tutorial', () => { - cy.getByTestSubj('obltOnboardingHomeStartApmTutorial').click(); - - cy.url().should('include', '/app/home#/tutorial/apm'); - }); - - it('navigates to kubernetes integration', () => { - cy.getByTestSubj('obltOnboardingHomeGoToKubernetesIntegration').click(); - - cy.url().should('include', '/app/integrations/detail/kubernetes/overview'); - }); - - it('navigates to integrations', () => { - cy.getByTestSubj('obltOnboardingHomeExploreIntegrations').click(); - - cy.url().should('include', '/app/integrations/browse'); - }); - }); - - describe('When user clicks on Quick links', () => { - it('navigates to use sample data', () => { - cy.getByTestSubj('obltOnboardingHomeUseSampleData').click(); - - cy.url().should('include', '/app/home#/tutorial_directory/sampleData'); - }); - - it('navigates to upload a file', () => { - cy.getByTestSubj('obltOnboardingHomeUploadAFile').click(); - - cy.url().should('include', '/app/home#/tutorial_directory/fileDataViz'); - }); - }); -}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/commands.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/commands.ts deleted file mode 100644 index dd07f4dc3dd4a..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/commands.ts +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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 URL from 'url'; -import { ObservabilityOnboardingUsername } from '@kbn/observability-onboarding-plugin/server/test_helpers/create_observability_onboarding_users/authentication'; - -export type InstallationStep = - | 'ea-download' - | 'ea-extract' - | 'ea-install' - | 'ea-status' - | 'ea-config'; - -export type InstallationStepStatus = - | 'incomplete' - | 'complete' - | 'disabled' - | 'loading' - | 'warning' - | 'danger' - | 'current'; - -export interface ElasticAgentStepPayload { - agentId: string; -} - -Cypress.Commands.add('loginAsViewerUser', () => { - return cy.loginAs({ - username: ObservabilityOnboardingUsername.viewerUser, - password: 'changeme', - }); -}); - -Cypress.Commands.add('loginAsEditorUser', () => { - return cy.loginAs({ - username: ObservabilityOnboardingUsername.editorUser, - password: 'changeme', - }); -}); - -Cypress.Commands.add('loginAsLogMonitoringUser', () => { - return cy.loginAs({ - username: ObservabilityOnboardingUsername.logMonitoringUser, - password: 'changeme', - }); -}); - -Cypress.Commands.add('loginAsElastic', () => { - return cy.loginAs({ - username: 'elastic', - password: 'changeme', - }); -}); - -Cypress.Commands.add( - 'loginAs', - ({ username, password }: { username: string; password: string }) => { - const kibanaUrl = Cypress.env('KIBANA_URL'); - cy.log(`Logging in as ${username} on ${kibanaUrl}`); - cy.visit('/'); - cy.request({ - log: true, - method: 'POST', - url: `${kibanaUrl}/internal/security/login`, - body: { - providerType: 'basic', - providerName: 'basic', - currentURL: `${kibanaUrl}/login`, - params: { username, password }, - }, - headers: { - 'kbn-xsrf': 'e2e_test', - }, - }); - cy.visit('/'); - } -); - -Cypress.Commands.add('getByTestSubj', (selector: string) => { - return cy.get(`[data-test-subj="${selector}"]`); -}); - -Cypress.Commands.add('visitKibana', (url: string, rangeFrom?: string, rangeTo?: string) => { - const urlPath = URL.format({ - pathname: url, - query: { rangeFrom, rangeTo }, - }); - - cy.visit(urlPath); - cy.getByTestSubj('kbnLoadingMessage').should('exist'); - cy.getByTestSubj('kbnLoadingMessage').should('not.exist', { - timeout: 50000, - }); -}); - -Cypress.Commands.add('installCustomIntegration', (integrationName: string) => { - const kibanaUrl = Cypress.env('KIBANA_URL'); - - cy.request({ - log: false, - method: 'POST', - url: `${kibanaUrl}/api/fleet/epm/custom_integrations`, - body: { - force: true, - integrationName, - datasets: [ - { name: `${integrationName}.access`, type: 'logs' }, - { name: `${integrationName}.error`, type: 'metrics' }, - { name: `${integrationName}.warning`, type: 'logs' }, - ], - }, - headers: { - 'kbn-xsrf': 'e2e_test', - 'Elastic-Api-Version': '2023-10-31', - }, - auth: { user: 'editor', pass: 'changeme' }, - }); -}); - -Cypress.Commands.add('deleteIntegration', (integrationName: string) => { - const kibanaUrl = Cypress.env('KIBANA_URL'); - - cy.request({ - log: false, - method: 'GET', - url: `${kibanaUrl}/api/fleet/epm/packages/${integrationName}`, - headers: { - 'kbn-xsrf': 'e2e_test', - }, - auth: { user: 'editor', pass: 'changeme' }, - failOnStatusCode: false, - }).then((response) => { - const status = response.body.item?.status; - if (status === 'installed') { - cy.request({ - log: false, - method: 'DELETE', - url: `${kibanaUrl}/api/fleet/epm/packages/${integrationName}`, - body: { - force: false, - }, - headers: { - 'kbn-xsrf': 'e2e_test', - 'Elastic-Api-Version': '2023-10-31', - }, - auth: { user: 'editor', pass: 'changeme' }, - }); - } - }); -}); - -Cypress.Commands.add( - 'updateInstallationStepStatus', - ( - onboardingId: string, - step: InstallationStep, - status: InstallationStepStatus, - payload: ElasticAgentStepPayload | undefined - ) => { - const kibanaUrl = Cypress.env('KIBANA_URL'); - - cy.log(onboardingId, step, status); - - cy.request({ - log: false, - method: 'POST', - url: `${kibanaUrl}/internal/observability_onboarding/flow/${onboardingId}/step/${step}`, - headers: { - 'kbn-xsrf': 'e2e_test', - }, - auth: { user: 'editor', pass: 'changeme' }, - body: { - status, - payload, - }, - }); - } -); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/types.d.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/types.d.ts deleted file mode 100644 index 7bb3549a60e7c..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/types.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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. - */ - -declare namespace Cypress { - interface Chainable { - loginAs(params: { - username: string; - password: string; - }): Cypress.Chainable>; - loginAsViewerUser(): Cypress.Chainable>; - loginAsEditorUser(): Cypress.Chainable>; - loginAsLogMonitoringUser(): Cypress.Chainable>; - loginAsElastic(): Cypress.Chainable>; - getByTestSubj(selector: string): Chainable>; - visitKibana(url: string, rangeFrom?: string, rangeTo?: string): void; - installCustomIntegration(integrationName: string): void; - deleteIntegration(integrationName: string): void; - updateInstallationStepStatus( - onboardingId: string, - step: InstallationStep, - status: InstallationStepStatus, - payload?: ElasticAgentStepPayload - ): void; - } -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress_test_runner.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress_test_runner.ts deleted file mode 100644 index 4a8326d81b246..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress_test_runner.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 cypress from 'cypress'; -import path from 'path'; -import Url from 'url'; -import { createObservabilityOnboardingUsers } from '@kbn/observability-onboarding-plugin/server/test_helpers/create_observability_onboarding_users'; -import { FtrProviderContext } from './ftr_provider_context'; - -export async function cypressTestRunner({ - ftrProviderContext: { getService }, - cypressExecution, -}: { - ftrProviderContext: FtrProviderContext; - cypressExecution: typeof cypress.run | typeof cypress.open; -}) { - const config = getService('config'); - - const username = config.get('servers.elasticsearch.username'); - const password = config.get('servers.elasticsearch.password'); - - const kibanaUrl = Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - auth: `${username}:${password}`, - }); - - const esNode = Url.format({ - protocol: config.get('servers.elasticsearch.protocol'), - port: config.get('servers.elasticsearch.port'), - hostname: config.get('servers.elasticsearch.hostname'), - auth: `${username}:${password}`, - }); - - // Creates ObservabilityOnboarding users - await createObservabilityOnboardingUsers({ - elasticsearch: { node: esNode, username, password }, - kibana: { hostname: kibanaUrl }, - }); - - const esRequestTimeout = config.get('timeouts.esRequestTimeout'); - - const kibanaUrlWithoutAuth = Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - }); - - const cypressProjectPath = path.join(__dirname); - const { open, ...cypressCliArgs } = getCypressCliArgs(); - - const res = await cypressExecution({ - ...cypressCliArgs, - project: cypressProjectPath, - config: { - e2e: { - baseUrl: kibanaUrlWithoutAuth, - }, - }, - env: { - KIBANA_URL: kibanaUrlWithoutAuth, - ES_NODE: esNode, - ES_REQUEST_TIMEOUT: esRequestTimeout, - TEST_CLOUD: process.env.TEST_CLOUD, - }, - }); - - return res; -} - -function getCypressCliArgs(): Record { - if (!process.env.CYPRESS_CLI_ARGS) { - return {}; - } - - const { $0, _, ...cypressCliArgs } = JSON.parse(process.env.CYPRESS_CLI_ARGS) as Record< - string, - unknown - >; - - const spec = - typeof cypressCliArgs.spec === 'string' && !cypressCliArgs.spec.includes('**') - ? `**/${cypressCliArgs.spec}*` - : cypressCliArgs.spec; - - return { ...cypressCliArgs, spec }; -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config.ts deleted file mode 100644 index 56cb76e2b2ac7..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 { FtrConfigProviderContext } from '@kbn/test'; -import { CA_CERT_PATH } from '@kbn/dev-utils'; -import { commonFunctionalServices } from '@kbn/ftr-common-functional-services'; -import { commonFunctionalUIServices } from '@kbn/ftr-common-functional-ui-services'; -import path from 'path'; - -const kibanaYamlFilePath = path.join(__dirname, './ftr_kibana.yml'); - -async function ftrConfig({ readConfigFile }: FtrConfigProviderContext) { - const kibanaCommonTestsConfig = await readConfigFile( - require.resolve('@kbn/test-suites-src/common/config') - ); - const xpackFunctionalTestsConfig = await readConfigFile( - require.resolve('@kbn/test-suites-xpack/functional/config.base') - ); - - return { - ...kibanaCommonTestsConfig.getAll(), - - services: { - ...commonFunctionalServices, - ...commonFunctionalUIServices, - }, - - esTestCluster: { - ...xpackFunctionalTestsConfig.get('esTestCluster'), - serverArgs: [ - ...xpackFunctionalTestsConfig.get('esTestCluster.serverArgs'), - // define custom es server here - // API Keys is enabled at the top level - 'xpack.security.enabled=true', - ], - }, - - kbnTestServer: { - ...xpackFunctionalTestsConfig.get('kbnTestServer'), - serverArgs: [ - ...xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs'), - '--home.disableWelcomeScreen=true', - '--csp.strict=false', - '--csp.warnLegacyBrowsers=false', - // define custom kibana server args here - `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, - `--config=${kibanaYamlFilePath}`, - ], - }, - }; -} - -// eslint-disable-next-line import/no-default-export -export default ftrConfig; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config_open.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config_open.ts deleted file mode 100644 index 18508abf27f0c..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config_open.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 { FtrConfigProviderContext } from '@kbn/test'; -import cypress from 'cypress'; -import { FtrProviderContext } from './ftr_provider_context'; -import { cypressTestRunner } from './cypress_test_runner'; - -async function ftrConfigOpen({ readConfigFile }: FtrConfigProviderContext) { - const kibanaConfig = await readConfigFile(require.resolve('./ftr_config.ts')); - return { - ...kibanaConfig.getAll(), - testRunner, - }; -} - -export async function testRunner(ftrProviderContext: FtrProviderContext) { - await cypressTestRunner({ - ftrProviderContext, - cypressExecution: cypress.open, - }); -} - -// eslint-disable-next-line import/no-default-export -export default ftrConfigOpen; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config_runner.ts b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config_runner.ts deleted file mode 100644 index 535062fdda0b0..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_config_runner.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 { FtrConfigProviderContext } from '@kbn/test'; -import cypress from 'cypress'; -import { cypressTestRunner } from './cypress_test_runner'; -import { FtrProviderContext } from './ftr_provider_context'; - -async function ftrConfigRun({ readConfigFile }: FtrConfigProviderContext) { - const kibanaConfig = await readConfigFile(require.resolve('./ftr_config.ts')); - - return { - ...kibanaConfig.getAll(), - testRunner, - }; -} - -async function testRunner(ftrProviderContext: FtrProviderContext) { - const result = await cypressTestRunner({ - ftrProviderContext, - cypressExecution: cypress.run, - }); - - if ( - result && - ((result as CypressCommandLine.CypressFailedRunResult)?.status === 'failed' || - (result as CypressCommandLine.CypressRunResult)?.totalFailed) - ) { - process.exit(1); - } -} - -// eslint-disable-next-line import/no-default-export -export default ftrConfigRun; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_kibana.yml b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_kibana.yml deleted file mode 100644 index 06507182d40fe..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_kibana.yml +++ /dev/null @@ -1 +0,0 @@ -xpack.cloud.id: 'myDeployment:03ce773830e104e139243c8f053c974d' diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/kibana.jsonc b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/kibana.jsonc deleted file mode 100644 index 033d72a02427f..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/kibana.jsonc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "type": "test-helper", - "id": "@kbn/observability-onboarding-e2e", - "owner": ["@elastic/obs-ux-logs-team"], - "group": "observability", - "visibility": "private", - "devOnly": true -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/tsconfig.json b/x-pack/solutions/observability/plugins/observability_onboarding/e2e/tsconfig.json deleted file mode 100644 index 33051d690a6f7..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "../../../../../../tsconfig.base.json", - "include": ["**/*"], - "exclude": ["tmp", "target/**/*"], - "compilerOptions": { - "outDir": "target/types", - "types": ["cypress", "node", "cypress-real-events"], - "isolatedModules": false - }, - "kbn_references": [ - { "path": "../../../../test/tsconfig.json" }, - "@kbn/test", - "@kbn/dev-utils", - "@kbn/cypress-config", - "@kbn/observability-onboarding-plugin", - "@kbn/ftr-common-functional-services", - "@kbn/ftr-common-functional-ui-services", - "@kbn/tooling-log" - ] -} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/tsconfig.json b/x-pack/solutions/observability/plugins/observability_onboarding/tsconfig.json index 391f1d6fee64f..79e54ce203d63 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/tsconfig.json +++ b/x-pack/solutions/observability/plugins/observability_onboarding/tsconfig.json @@ -8,7 +8,8 @@ "public/**/*", "../../../../../typings/**/*", "public/**/*.json", - "server/**/*" + "server/**/*", + "e2e/**/*" ], "kbn_references": [ "@kbn/core", @@ -43,7 +44,8 @@ "@kbn/server-route-repository-utils", "@kbn/core-application-browser", "@kbn/core-plugins-server", - "@kbn/logs-shared-plugin" + "@kbn/logs-shared-plugin", + "@kbn/tooling-log" ], "exclude": [ "target/**/*" diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/README.md b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/README.md new file mode 100644 index 0000000000000..9a45d498a61eb --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/README.md @@ -0,0 +1,23 @@ +## How to run tests + +First start the servers: + +```bash +// ESS +node scripts/scout.js start-server --stateful + +// Serverless +node scripts/scout.js start-server --serverless=[es|oblt|security] +``` + +Then you can run the tests in another terminal: + +```bash +// ESS +npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts --grep @ess + +// Serverless +npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts --grep @svlOblt +``` + +Test results are available in `x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/output` diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/index.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/index.ts new file mode 100644 index 0000000000000..a928c1d6fe34b --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/index.ts @@ -0,0 +1,50 @@ +/* + * 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 { + test as base, + PageObjects, + createLazyPageObject, + ScoutTestFixtures, + ScoutWorkerFixtures, + KibanaUrl, + KbnClient, +} from '@kbn/scout'; +import { OnboardingHomePage } from './page_objects'; +import { CustomLogsPage } from './page_objects/custom_logs'; + +export interface ExtendedScoutTestFixtures extends ScoutTestFixtures { + pageObjects: PageObjects & { + onboardingHomePage: OnboardingHomePage; + customLogsPage: CustomLogsPage; + }; +} + +export const test = base.extend({ + pageObjects: async ( + { + pageObjects, + page, + kbnUrl, + kbnClient, + }: { + pageObjects: ExtendedScoutTestFixtures['pageObjects']; + page: ExtendedScoutTestFixtures['page']; + kbnUrl: KibanaUrl; + kbnClient: KbnClient; + }, + use: (pageObjects: ExtendedScoutTestFixtures['pageObjects']) => Promise + ) => { + const extendedPageObjects = { + ...pageObjects, + onboardingHomePage: createLazyPageObject(OnboardingHomePage, page), + customLogsPage: createLazyPageObject(CustomLogsPage, page, kbnUrl, kbnClient), + }; + + await use(extendedPageObjects); + }, +}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/custom_logs.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/custom_logs.ts new file mode 100644 index 0000000000000..1b2b8119e5ce2 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/custom_logs.ts @@ -0,0 +1,195 @@ +/* + * 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 { ScoutPage, KibanaUrl, KbnClient } from '@kbn/scout'; + +export class CustomLogsPage { + static readonly ASSERTION_MESSAGES = { + INTEGRATION_NAME_CASE_ERROR: 'An integration name should be lowercase.', + DATASET_NAME_CASE_ERROR: 'A dataset name should be lowercase.', + EXISTING_INTEGRATION_ERROR: (name: string) => + `Failed to create the integration as an installation with the name ${name} already exists.`, + + DOWNLOADING_AGENT_STATUS: 'Downloading Elastic Agent', + DOWNLOADED_AGENT_STATUS: 'Elastic Agent downloaded', + DOWNLOAD_AGENT_DANGER_CALLOUT: 'Download Elastic Agent', + EXTRACTING_AGENT_STATUS: 'Extracting Elastic Agent', + EXTRACTED_AGENT_STATUS: 'Elastic Agent extracted', + EXTRACT_AGENT_DANGER_CALLOUT: 'Extract Elastic Agent', + INSTALLING_AGENT_STATUS: 'Installing Elastic Agent', + INSTALLED_AGENT_STATUS: 'Elastic Agent installed', + INSTALL_AGENT_DANGER_CALLOUT: 'Install Elastic Agent', + CONNECTING_TO_AGENT_STATUS: 'Connecting to the Elastic Agent', + CONNECTED_TO_AGENT_STATUS: 'Connected to the Elastic Agent', + CONNECT_AGENT_WARNING_CALLOUT: 'Connect to the Elastic Agent', + DOWNLOAD_AGENT_CONFIG_STATUS: 'Downloading Elastic Agent config', + CONFIGURE_AGENT_WARNING_CALLOUT: 'Configure the agent', + DOWNLOADING_AGENT_CONFIG_STATUS: 'Downloading Elastic Agent config', + AGENT_CONFIGURATION_SUCCESS_CALLOUT_MACOS: + 'Elastic Agent config written to /Library/Elastic/Agent/elastic-agent.yml', + AGENT_CONFIGURATION_SUCCESS_CALLOUT_LINUX: + 'Elastic Agent config written to /opt/Elastic/Agent/elastic-agent.yml', + INSTALLATION_STEP_2_DISABLED: 'Step 2 is disabled', + }; + + public readonly advancedSettingsContent; + public readonly logFilePathList; + public readonly continueButton; + public readonly addLogFilePathButton; + public readonly integrationNameInput; + public readonly datasetNameInput; + public readonly serviceNameInput; + public readonly namespaceInput; + public readonly customConfigInput; + public readonly customIntegrationSuccessCallout; + public readonly customIntegrationErrorCallout; + public readonly apiKeyCreateSuccessCallout; + public readonly apiKeyPrivilegesErrorCallout; + public readonly apiKeyCreateErrorCallout; + public readonly linuxCodeSnippetButton; + public readonly macOSCodeSnippetButton; + public readonly windowsCodeSnippetButton; + public readonly autoDownloadConfigurationToggle; + public readonly autoDownloadConfigurationCallout; + public readonly installCodeSnippet; + public readonly windowsInstallElasticAgentDocLink; + public readonly configureElasticAgentStep; + public readonly downloadConfigurationButton; + public readonly stepStatusLoading; + public readonly stepStatusComplete; + public readonly stepStatusDanger; + public readonly stepStatusWarning; + + constructor( + private readonly page: ScoutPage, + private readonly kbnUrl: KibanaUrl, + private readonly kbnClient: KbnClient + ) { + this.advancedSettingsContent = this.page.testSubj + .locator('obltOnboardingCustomLogsAdvancedSettings') + .getByRole('group'); + this.logFilePathList = this.page.locator(`[data-test-subj^=obltOnboardingLogFilePath-]`); + this.continueButton = this.page.testSubj.locator('obltOnboardingCustomLogsContinue'); + this.addLogFilePathButton = this.page.testSubj.locator('obltOnboardingCustomLogsAddFilePath'); + this.integrationNameInput = this.page.testSubj.locator( + 'obltOnboardingCustomLogsIntegrationsName' + ); + this.datasetNameInput = this.page.testSubj.locator('obltOnboardingCustomLogsDatasetName'); + this.serviceNameInput = this.page.testSubj.locator('obltOnboardingCustomLogsServiceName'); + this.namespaceInput = this.page.testSubj.locator('obltOnboardingCustomLogsNamespace'); + this.customConfigInput = this.page.testSubj.locator('obltOnboardingCustomLogsCustomConfig'); + this.customIntegrationSuccessCallout = this.page.testSubj.locator( + 'obltOnboardingCustomIntegrationInstalled' + ); + this.customIntegrationErrorCallout = this.page.testSubj.locator( + 'obltOnboardingCustomIntegrationErrorCallout' + ); + this.apiKeyCreateSuccessCallout = this.page.testSubj.locator('obltOnboardingLogsApiKeyCreated'); + this.apiKeyPrivilegesErrorCallout = this.page.testSubj.locator( + 'obltOnboardingLogsApiKeyCreationNoPrivileges' + ); + this.apiKeyCreateErrorCallout = this.page.testSubj.locator( + 'obltOnboardingLogsApiKeyCreationFailed' + ); + this.linuxCodeSnippetButton = this.page.testSubj.locator('linux-tar'); + this.macOSCodeSnippetButton = this.page.testSubj.locator('macos'); + this.windowsCodeSnippetButton = this.page.testSubj.locator('windows'); + this.autoDownloadConfigurationToggle = this.page.testSubj.locator( + 'obltOnboardingInstallElasticAgentAutoDownloadConfig' + ); + this.autoDownloadConfigurationCallout = this.page.testSubj.locator( + 'obltOnboardingInstallElasticAgentAutoDownloadConfigCallout' + ); + this.installCodeSnippet = this.page.testSubj + .locator('obltOnboardingInstallElasticAgentStep') + .getByRole('code'); + this.windowsInstallElasticAgentDocLink = this.page.testSubj.locator( + 'obltOnboardingInstallElasticAgentWindowsDocsLink' + ); + this.configureElasticAgentStep = this.page.testSubj.locator( + 'obltOnboardingConfigureElasticAgentStep' + ); + this.downloadConfigurationButton = this.page.testSubj.locator( + 'obltOnboardingConfigureElasticAgentStepDownloadConfig' + ); + this.stepStatusLoading = this.page.testSubj.locator('obltOnboardingStepStatus-loading'); + this.stepStatusComplete = this.page.testSubj.locator('obltOnboardingStepStatus-complete'); + this.stepStatusDanger = this.page.testSubj.locator('obltOnboardingStepStatus-danger'); + this.stepStatusWarning = this.page.testSubj.locator('obltOnboardingStepStatus-warning'); + } + + async goto() { + this.page.goto(`${this.kbnUrl.app('observabilityOnboarding')}/customLogs`); + } + + async clickBackButton() { + await this.page.testSubj.click('observabilityOnboardingFlowBackToSelectionButton'); + } + + logFilePathInput(index: number) { + return this.page.testSubj.locator(`obltOnboardingLogFilePath-${index}`).getByRole('textbox'); + } + + logFilePathDeleteButton(index: number) { + return this.page.testSubj.locator(`obltOnboardingLogFilePathDelete-${index}`); + } + + async clickAdvancedSettingsButton() { + return this.page.testSubj + .locator('obltOnboardingCustomLogsAdvancedSettings') + .getByRole('button') + .first() + .click(); + } + + async installCustomIntegration(name: string) { + await this.kbnClient.request({ + method: 'POST', + path: `/api/fleet/epm/custom_integrations`, + body: { + force: true, + integrationName: name, + datasets: [ + { name: `${name}.access`, type: 'logs' }, + { name: `${name}.error`, type: 'metrics' }, + { name: `${name}.warning`, type: 'logs' }, + ], + }, + }); + } + + async deleteIntegration(name: string) { + const packageInfo = await this.kbnClient.request<{ item: { status: string } }>({ + method: 'GET', + path: `/api/fleet/epm/packages/${name}`, + ignoreErrors: [404], + }); + + if (packageInfo.data.item?.status === 'installed') { + await this.kbnClient.request({ + method: 'DELETE', + path: `/api/fleet/epm/packages/${name}`, + }); + } + } + + async updateInstallationStepStatus( + onboardingId: string, + step: string, + status: string, + payload?: object + ) { + await this.kbnClient.request({ + method: 'POST', + path: `/internal/observability_onboarding/flow/${onboardingId}/step/${step}`, + body: { + status, + payload, + }, + }); + } +} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/e2e.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/index.ts similarity index 72% rename from x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/e2e.ts rename to x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/index.ts index 5f5d1eb3b3614..c032371659593 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/support/e2e.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/index.ts @@ -5,8 +5,4 @@ * 2.0. */ -Cypress.on('uncaught:exception', (err, runnable) => { - return false; -}); - -import './commands'; +export { OnboardingHomePage } from './onboarding_home'; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/onboarding_home.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/onboarding_home.ts new file mode 100644 index 0000000000000..bd35dc4fbe909 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/onboarding_home.ts @@ -0,0 +1,16 @@ +/* + * 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 { ScoutPage } from '@kbn/scout'; + +export class OnboardingHomePage { + constructor(private readonly page: ScoutPage) {} + + async goto() { + this.page.gotoApp('observabilityOnboarding'); + } +} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_provider_context.d.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts similarity index 59% rename from x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_provider_context.d.ts rename to x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts index b87f35adcccf2..34b370396b67e 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/e2e/ftr_provider_context.d.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts @@ -5,6 +5,9 @@ * 2.0. */ -import { GenericFtrProviderContext } from '@kbn/test'; +import { createPlaywrightConfig } from '@kbn/scout'; -export type FtrProviderContext = GenericFtrProviderContext<{}, {}>; +// eslint-disable-next-line import/no-default-export +export default createPlaywrightConfig({ + testDir: './tests', +}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/configuration.spec.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/configuration.spec.ts new file mode 100644 index 0000000000000..2a2db5af2c7ec --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/configuration.spec.ts @@ -0,0 +1,256 @@ +/* + * 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/scout'; +import { test } from '../../fixtures'; +import { CustomLogsPage } from '../../fixtures/page_objects/custom_logs'; + +test.describe('Onboarding app - Custom logs configuration', { tag: ['@ess', '@svlOblt'] }, () => { + test.beforeEach(async ({ browserAuth, pageObjects: { customLogsPage } }) => { + await browserAuth.loginAsAdmin(); + await customLogsPage.goto(); + }); + + test('When user clicks the back button user goes to the onboarding home page', async ({ + pageObjects: { customLogsPage }, + page, + }) => { + await customLogsPage.clickBackButton(); + expect(page.url()).toContain('/app/observabilityOnboarding'); + }); + + test(`Users shouldn't be able to continue if logFilePaths is empty`, async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.logFilePathInput(0)).toHaveValue(''); + await expect(customLogsPage.continueButton).toBeDisabled(); + }); + + test(`Users should be able to continue if logFilePaths is not empty`, async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.logFilePathInput(0).fill('some/path'); + + await expect(customLogsPage.continueButton).not.toBeDisabled(); + }); + + test('Users can add multiple logFilePaths', async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.addLogFilePathButton.click(); + await expect(customLogsPage.logFilePathInput(0)).toBeVisible(); + await expect(customLogsPage.logFilePathInput(1)).toBeVisible(); + }); + + test('Users can delete logFilePaths', async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.addLogFilePathButton.click(); + await expect(customLogsPage.logFilePathList).toHaveCount(2); + + await customLogsPage.logFilePathDeleteButton(1).click(); + await expect(customLogsPage.logFilePathList).toHaveCount(1); + }); + + test('Dataset Name and Integration Name are auto generated if it is the first path', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.logFilePathInput(0).fill('myLogs.log'); + + await expect(customLogsPage.integrationNameInput).toHaveValue('mylogs'); + await expect(customLogsPage.datasetNameInput).toHaveValue('mylogs'); + }); + + test('Dataset Name and Integration Name are not generated if it is not the first path', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.addLogFilePathButton.click(); + await customLogsPage.logFilePathInput(1).fill('myLogs.log'); + + await expect(customLogsPage.integrationNameInput).toHaveValue(''); + await expect(customLogsPage.datasetNameInput).toHaveValue(''); + }); + + test('Service name input should be optional allowing user to continue if it is empty', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.logFilePathInput(0).fill('myLogs.log'); + + await expect(customLogsPage.serviceNameInput).toHaveValue(''); + await expect(customLogsPage.continueButton).not.toBeDisabled(); + }); + + test('Users should expand and collapse the Advanced settings section', async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.advancedSettingsContent).not.toBeVisible(); + + await customLogsPage.clickAdvancedSettingsButton(); + + await expect(customLogsPage.advancedSettingsContent).toBeVisible(); + + await customLogsPage.clickAdvancedSettingsButton(); + + await expect(customLogsPage.advancedSettingsContent).not.toBeVisible(); + }); + + test('Users should see a default namespace', async ({ pageObjects: { customLogsPage } }) => { + customLogsPage.clickAdvancedSettingsButton(); + + await expect(customLogsPage.namespaceInput).toHaveValue('default'); + }); + + test('Users should not be able to continue if they do not specify a namespace', async ({ + pageObjects: { customLogsPage }, + }) => { + customLogsPage.clickAdvancedSettingsButton(); + + await customLogsPage.namespaceInput.fill(''); + await expect(customLogsPage.continueButton).toBeDisabled(); + }); + + test('should be optional allowing user to continue if it is empty', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.clickAdvancedSettingsButton(); + await customLogsPage.logFilePathInput(0).fill('myLogs.log'); + await expect(customLogsPage.customConfigInput).toHaveValue(''); + await expect(customLogsPage.continueButton).not.toBeDisabled(); + }); + + test.describe('Integration name', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.logFilePathInput(0).fill('myLogs.log'); + }); + + test('Users should not be able to continue if they do not specify an integrationName', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.integrationNameInput.fill(''); + await expect(customLogsPage.continueButton).toBeDisabled(); + }); + + test('value will contain _ instead of special chars', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.integrationNameInput.fill('hello$world'); + await expect(customLogsPage.integrationNameInput).toHaveValue('hello_world'); + }); + + test('value will be invalid if it is not lowercase', async ({ + pageObjects: { customLogsPage }, + page, + }) => { + await customLogsPage.integrationNameInput.fill('H3llowOrld'); + await expect( + page.getByText(CustomLogsPage.ASSERTION_MESSAGES.INTEGRATION_NAME_CASE_ERROR) + ).toBeVisible(); + }); + }); + + test.describe('datasetName', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.logFilePathInput(0).fill('myLogs.log'); + }); + + test('Users should not be able to continue if they do not specify a datasetName', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.datasetNameInput.fill(''); + await expect(customLogsPage.continueButton).toBeDisabled(); + }); + + test('value will contain _ instead of special chars', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.datasetNameInput.fill('hello$world'); + await expect(customLogsPage.datasetNameInput).toHaveValue('hello_world'); + }); + + test('value will be invalid if it is not lowercase', async ({ + pageObjects: { customLogsPage }, + page, + }) => { + await customLogsPage.datasetNameInput.fill('H3llowOrld'); + await expect( + page.getByText(CustomLogsPage.ASSERTION_MESSAGES.DATASET_NAME_CASE_ERROR) + ).toBeVisible(); + }); + }); + + test.describe('Custom integration', () => { + const CUSTOM_INTEGRATION_NAME = 'mylogs'; + + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.deleteIntegration(CUSTOM_INTEGRATION_NAME); + }); + + test.describe('when user is missing privileges', () => { + test.beforeEach(async ({ browserAuth, page }) => { + await browserAuth.loginAsViewer(); + await page.reload(); + }); + + test.afterEach(async ({ browserAuth, page }) => { + await browserAuth.loginAsAdmin(); + await page.reload(); + }); + + test('installation fails', async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`); + await customLogsPage.continueButton.click(); + + await expect(customLogsPage.customIntegrationErrorCallout).toBeVisible(); + }); + }); + + test.describe('when user has proper privileges', () => { + test('installation succeed and user is redirected to install elastic agent step', async ({ + page, + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`); + await customLogsPage.continueButton.click(); + + await page.waitForURL('**/app/observabilityOnboarding/customLogs/installElasticAgent'); + }); + }); + + test('installation fails if integration already exists', async ({ + pageObjects: { customLogsPage }, + page, + }) => { + await customLogsPage.installCustomIntegration(CUSTOM_INTEGRATION_NAME); + await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`); + await customLogsPage.continueButton.click(); + + await expect( + page.getByText( + CustomLogsPage.ASSERTION_MESSAGES.EXISTING_INTEGRATION_ERROR(CUSTOM_INTEGRATION_NAME) + ) + ).toBeVisible(); + }); + + test.describe('when an error occurred on creation', () => { + test('user should see the error displayed', async ({ + pageObjects: { customLogsPage }, + page, + kbnUrl, + }) => { + await page.route(kbnUrl.get('/api/fleet/epm/custom_integrations'), (route) => { + route.fulfill({ + status: 500, + json: { + message: 'Internal error', + }, + }); + }); + + await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`); + await customLogsPage.continueButton.click(); + + await expect(customLogsPage.customIntegrationErrorCallout).toBeVisible(); + }); + }); + }); +}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/install_elastic_agent.spec.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/install_elastic_agent.spec.ts new file mode 100644 index 0000000000000..2dadb23090418 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/install_elastic_agent.spec.ts @@ -0,0 +1,696 @@ +/* + * 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/scout'; +import { type ExtendedScoutTestFixtures, test } from '../../fixtures'; +import { CustomLogsPage } from '../../fixtures/page_objects/custom_logs'; + +const CUSTOM_INTEGRATION_NAME = 'mylogs'; + +function setupPage({ isAdmin }: { isAdmin: boolean }) { + return async ({ + browserAuth, + pageObjects: { customLogsPage }, + page, + }: ExtendedScoutTestFixtures) => { + await browserAuth.loginAsAdmin(); + await customLogsPage.deleteIntegration(CUSTOM_INTEGRATION_NAME); + + if (!isAdmin) { + await browserAuth.loginAsPrivilegedUser(); + await page.reload(); + } + + await customLogsPage.deleteIntegration(CUSTOM_INTEGRATION_NAME); + + await customLogsPage.goto(); + await customLogsPage.logFilePathInput(0).fill('mylogs.log'); + await customLogsPage.continueButton.click(); + }; +} + +test.describe( + 'Onboarding app - Custom logs install Elastic Agent', + { tag: ['@ess', '@svlOblt'] }, + () => { + test.describe('Custom integration', () => { + test.beforeEach(setupPage({ isAdmin: true })); + + test('Users should be able to see the custom integration success callout', async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.customIntegrationSuccessCallout).toBeVisible(); + }); + }); + + test.describe('ApiKey generation', () => { + test.describe('when user is missing privileges', () => { + test.beforeEach(setupPage({ isAdmin: false })); + + test('apiKey is not generated', async ({ pageObjects: { customLogsPage } }) => { + await expect(customLogsPage.apiKeyPrivilegesErrorCallout).toBeVisible(); + }); + }); + + test.describe('when user has proper privileges', () => { + test.beforeEach(setupPage({ isAdmin: true })); + + test('apiKey is generated', async ({ pageObjects: { customLogsPage } }) => { + await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible(); + }); + }); + + test.describe('when an error occurred on creation', () => { + test.beforeEach(async ({ browserAuth, pageObjects: { customLogsPage }, page }) => { + await page.route('**/observability_onboarding/logs/flow', (route) => { + route.fulfill({ + status: 500, + body: JSON.stringify({ + message: 'Internal error', + }), + }); + }); + }); + + test.beforeEach(setupPage({ isAdmin: true })); + + test('apiKey is not generated', async ({ pageObjects: { customLogsPage } }) => { + await expect(customLogsPage.apiKeyCreateErrorCallout).toBeVisible(); + }); + }); + }); + + test.describe('Install the Elastic Agent step', () => { + test.beforeEach(setupPage({ isAdmin: true })); + + test.describe('When user select Linux OS', () => { + test('Auto download config to host is disabled by default', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.linuxCodeSnippetButton.click(); + await expect(customLogsPage.autoDownloadConfigurationToggle).not.toBeChecked(); + }); + + test('Installation script is shown', async ({ pageObjects: { customLogsPage } }) => { + await expect(customLogsPage.installCodeSnippet).toBeVisible(); + }); + }); + + test.describe('When user select Mac OS', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.macOSCodeSnippetButton.click(); + }); + + test('Auto download config to host is disabled by default', async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.autoDownloadConfigurationToggle).not.toBeChecked(); + }); + + test('Installation script is shown', async ({ pageObjects: { customLogsPage } }) => { + await expect(customLogsPage.installCodeSnippet).toBeVisible(); + }); + }); + + test.describe('When user select Windows OS', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.windowsCodeSnippetButton.click(); + }); + + test('Auto download config to host is disabled by default', async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.autoDownloadConfigurationToggle).not.toBeChecked(); + }); + + test('A link to the documentation is shown instead of installation script', async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.windowsInstallElasticAgentDocLink).toBeVisible(); + await expect(customLogsPage.installCodeSnippet).not.toBeVisible(); + }); + }); + + test.describe('When Auto download config', () => { + test.describe('is selected', () => { + test('autoDownloadConfig flag is added to installation script', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.autoDownloadConfigurationToggle.click(); + await expect(customLogsPage.autoDownloadConfigurationCallout).toBeVisible(); + await expect(customLogsPage.installCodeSnippet).toContainText('autoDownloadConfig=1'); + }); + + test('Download config button is disabled', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.autoDownloadConfigurationToggle.click(); + await expect(customLogsPage.downloadConfigurationButton).toBeDisabled(); + }); + }); + + test('is not selected autoDownloadConfig flag is not added to installation script', async ({ + pageObjects: { customLogsPage }, + }) => { + await expect(customLogsPage.installCodeSnippet).not.toContainText('autoDownloadConfig=1'); + }); + }); + + test.describe('When user executes the installation script in the host', () => { + let onboardingId: string; + + test.describe('updates on steps are shown in the flow', () => { + test.beforeEach(async ({ page, pageObjects: { customLogsPage } }) => { + await page.route('**/observability_onboarding/logs/flow', async (route) => { + const response = await route.fetch(); + const body = await response.json(); + + onboardingId = body.onboardingId; + + await route.fulfill({ response }); + }); + + await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible(); + }); + + test.describe('Download elastic Agent step', () => { + test('shows a loading callout when elastic agent is downloading', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'loading' + ); + await expect( + customLogsPage.stepStatusLoading.getByText( + CustomLogsPage.ASSERTION_MESSAGES.DOWNLOADING_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a success callout when elastic agent is downloaded', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await expect( + customLogsPage.stepStatusComplete.getByText( + CustomLogsPage.ASSERTION_MESSAGES.DOWNLOADED_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a danger callout when elastic agent was not downloaded', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'danger' + ); + await expect( + customLogsPage.stepStatusDanger.getByText( + CustomLogsPage.ASSERTION_MESSAGES.DOWNLOAD_AGENT_DANGER_CALLOUT + ) + ).toBeVisible(); + }); + }); + + test.describe('Extract elastic Agent step', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + }); + + test('shows a loading callout when elastic agent is extracting', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'loading' + ); + await expect( + customLogsPage.stepStatusLoading.getByText( + CustomLogsPage.ASSERTION_MESSAGES.EXTRACTING_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a success callout when elastic agent is extracted', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'complete' + ); + await expect( + customLogsPage.stepStatusComplete.getByText( + CustomLogsPage.ASSERTION_MESSAGES.EXTRACTED_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a danger callout when elastic agent was not extracted', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'danger' + ); + await expect( + customLogsPage.stepStatusDanger.getByText( + CustomLogsPage.ASSERTION_MESSAGES.EXTRACT_AGENT_DANGER_CALLOUT + ) + ).toBeVisible(); + }); + }); + + test.describe('Install elastic Agent step', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'complete' + ); + }); + + test('shows a loading callout when elastic agent is installing', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-install', + 'loading' + ); + await expect( + customLogsPage.stepStatusLoading.getByText( + CustomLogsPage.ASSERTION_MESSAGES.INSTALLING_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a success callout when elastic agent is installed', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-install', + 'complete' + ); + await expect( + customLogsPage.stepStatusComplete.getByText( + CustomLogsPage.ASSERTION_MESSAGES.INSTALLED_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a danger callout when elastic agent was not installed', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-install', + 'danger' + ); + await expect( + customLogsPage.stepStatusDanger.getByText( + CustomLogsPage.ASSERTION_MESSAGES.INSTALL_AGENT_DANGER_CALLOUT + ) + ).toBeVisible(); + }); + }); + + test.describe('Check elastic Agent status step', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-install', + 'complete' + ); + }); + + test('shows a loading callout when getting elastic agent status', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-status', + 'loading' + ); + await expect( + customLogsPage.stepStatusLoading.getByText( + CustomLogsPage.ASSERTION_MESSAGES.CONNECTING_TO_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a success callout when elastic agent status is healthy', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-status', + 'complete', + { + agentId: 'test-agent-id', + } + ); + await expect( + customLogsPage.stepStatusComplete.getByText( + CustomLogsPage.ASSERTION_MESSAGES.CONNECTED_TO_AGENT_STATUS + ) + ).toBeVisible(); + }); + + test('shows a warning callout when elastic agent status is not healthy', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-status', + 'warning' + ); + await expect( + customLogsPage.stepStatusWarning.getByText( + CustomLogsPage.ASSERTION_MESSAGES.CONNECT_AGENT_WARNING_CALLOUT + ) + ).toBeVisible(); + }); + }); + }); + }); + }); + + test.describe('Configure Elastic Agent step', () => { + let onboardingId: string; + + test.beforeEach(setupPage({ isAdmin: true })); + + test.beforeEach(async ({ page, pageObjects: { customLogsPage } }) => { + await page.route('**/observability_onboarding/logs/flow', async (route) => { + const response = await route.fetch(); + const body = await response.json(); + + onboardingId = body.onboardingId; + + await route.fulfill({ response }); + }); + + await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible(); + }); + + test.describe('When user select Linux OS', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.autoDownloadConfigurationToggle.click(); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { + agentId: 'test-agent-id', + }); + }); + + test('shows loading callout when config is being downloaded to the host', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading'); + await expect( + customLogsPage.stepStatusLoading.getByText( + CustomLogsPage.ASSERTION_MESSAGES.DOWNLOAD_AGENT_CONFIG_STATUS + ) + ).toBeVisible(); + }); + + test('shows success callout when the configuration has been written to the host', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); + await expect( + customLogsPage.stepStatusComplete.getByText( + CustomLogsPage.ASSERTION_MESSAGES.AGENT_CONFIGURATION_SUCCESS_CALLOUT_LINUX + ) + ).toBeVisible(); + }); + + test('shows warning callout when the configuration was not written in the host', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning'); + await expect( + customLogsPage.stepStatusWarning.getByText( + CustomLogsPage.ASSERTION_MESSAGES.CONFIGURE_AGENT_WARNING_CALLOUT + ) + ).toBeVisible(); + }); + }); + + test.describe('When user select Mac OS', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.macOSCodeSnippetButton.click(); + await customLogsPage.autoDownloadConfigurationToggle.click(); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { + agentId: 'test-agent-id', + }); + }); + + test('shows loading callout when config is being downloaded to the host', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading'); + await expect( + customLogsPage.stepStatusLoading.getByText( + CustomLogsPage.ASSERTION_MESSAGES.DOWNLOADING_AGENT_CONFIG_STATUS + ) + ).toBeVisible(); + }); + + test('shows success callout when the configuration has been written to the host', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); + await expect( + customLogsPage.stepStatusComplete.getByText( + CustomLogsPage.ASSERTION_MESSAGES.AGENT_CONFIGURATION_SUCCESS_CALLOUT_MACOS + ) + ).toBeVisible(); + }); + + test('shows warning callout when the configuration was not written in the host', async ({ + pageObjects: { customLogsPage }, + }) => { + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning'); + await expect( + customLogsPage.stepStatusWarning.getByText( + CustomLogsPage.ASSERTION_MESSAGES.CONFIGURE_AGENT_WARNING_CALLOUT + ) + ).toBeVisible(); + }); + }); + + test.describe('When user select Windows', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + customLogsPage.windowsCodeSnippetButton.click(); + }); + + test('step is disabled', async ({ pageObjects: { customLogsPage } }) => { + await expect( + customLogsPage.configureElasticAgentStep.getByText( + CustomLogsPage.ASSERTION_MESSAGES.INSTALLATION_STEP_2_DISABLED + ) + ).toBeVisible(); + }); + }); + }); + + test.describe('Check logs step', () => { + let onboardingId: string; + + test.beforeEach(setupPage({ isAdmin: true })); + + test.beforeEach(async ({ page, pageObjects: { customLogsPage } }) => { + await page.route('**/observability_onboarding/logs/flow', async (route) => { + const response = await route.fetch(); + const body = await response.json(); + + onboardingId = body.onboardingId; + + await route.fulfill({ response }); + }); + + await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible(); + }); + + test.describe('When user select Linux OS or MacOS', () => { + test.describe('When configure Elastic Agent step is not finished', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-install', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading'); + }); + + test('check logs is not triggered', async ({ page }) => { + await expect( + page.locator( + '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-incomplete"]' + ) + ).toBeVisible(); + await expect( + page.locator('.euiStep__title', { hasText: 'Ship logs to Elastic Observability' }) + ).toBeVisible(); + }); + }); + + test.describe('When configure Elastic Agent step has finished', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-download', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-extract', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-install', + 'complete' + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-status', + 'complete', + { + agentId: 'test-agent-id', + } + ); + await customLogsPage.updateInstallationStepStatus( + onboardingId, + 'ea-config', + 'complete' + ); + }); + + test('shows loading callout when logs are being checked', async ({ page }) => { + await expect( + page.locator( + '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]' + ) + ).toBeVisible(); + await expect( + page.locator('.euiStep__title', { hasText: 'Waiting for logs to be shipped...' }) + ).toBeVisible(); + }); + }); + }); + + test.describe('When user select Windows', () => { + test.beforeEach(async ({ pageObjects: { customLogsPage } }) => { + await customLogsPage.windowsCodeSnippetButton.click(); + }); + + test('step is disabled', async ({ pageObjects: { customLogsPage }, page }) => { + await expect( + page.locator( + '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]' + ) + ).toBeVisible(); + }); + }); + }); + + test.describe('When logs are being shipped', () => { + test.beforeEach(async ({ page }) => { + await page.route('**/progress', (route) => { + route.fulfill({ + status: 200, + body: JSON.stringify({ + progress: { + 'ea-download': { status: 'complete' }, + 'ea-extract': { status: 'complete' }, + 'ea-install': { status: 'complete' }, + 'ea-status': { status: 'complete' }, + 'ea-config': { status: 'complete' }, + 'logs-ingest': { status: 'complete' }, + }, + }), + }); + }); + }); + + test.beforeEach(setupPage({ isAdmin: true })); + + test('shows success callout when logs have arrived to elastic', async ({ page }) => { + await expect( + page.locator( + '[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]' + ) + ).toBeVisible(); + await expect( + page.locator('.euiStep__title', { hasText: 'Logs are being shipped!' }) + ).toBeVisible(); + }); + + test('when user clicks on Explore Logs it navigates to observability Discover', async ({ + page, + }) => { + await page.locator('[data-test-subj="obltOnboardingExploreLogs"]').click(); + await expect(page).toHaveURL(/\/app\/discover/); + }); + }); + } +); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tsconfig.json b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tsconfig.json new file mode 100644 index 0000000000000..6f9ee7501fef3 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "**/*" + ], + "kbn_references": [ + "@kbn/scout" + ], + "exclude": [ + "target/**/*" + ] +} diff --git a/yarn.lock b/yarn.lock index 2132e341a9a42..3bdf5c9e68c2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6525,10 +6525,6 @@ version "0.0.0" uid "" -"@kbn/observability-onboarding-e2e@link:x-pack/solutions/observability/plugins/observability_onboarding/e2e": - version "0.0.0" - uid "" - "@kbn/observability-onboarding-plugin@link:x-pack/solutions/observability/plugins/observability_onboarding": version "0.0.0" uid ""