diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 518192fbe05f3..f6164da4ac206 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -49,6 +49,8 @@ disabled: - x-pack/plugins/apm/ftr_e2e/ftr_config_open.ts - x-pack/plugins/apm/ftr_e2e/ftr_config_run.ts - x-pack/plugins/apm/ftr_e2e/ftr_config.ts + - x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts + - x-pack/test_serverless/functional/test_suites/observability/cypress/config_runner.ts - x-pack/plugins/profiling/e2e/ftr_config_open.ts - x-pack/plugins/profiling/e2e/ftr_config_runner.ts - x-pack/plugins/profiling/e2e/ftr_config.ts diff --git a/.buildkite/scripts/steps/functional/serverless_ftr.sh b/.buildkite/scripts/steps/functional/serverless_ftr.sh index 49c597ff065d5..f584ce33d6599 100755 --- a/.buildkite/scripts/steps/functional/serverless_ftr.sh +++ b/.buildkite/scripts/steps/functional/serverless_ftr.sh @@ -15,6 +15,7 @@ elif [[ "$SERVERLESS_ENVIRONMENT" == "observability" ]]; then SERVERLESS_CONFIGS=( "x-pack/test_serverless/api_integration/test_suites/observability/config.ts" "x-pack/test_serverless/functional/test_suites/observability/config.ts" + "x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts" ) elif [[ "$SERVERLESS_ENVIRONMENT" == "security" ]]; then SERVERLESS_CONFIGS=( diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/.eslintrc.json b/x-pack/test_serverless/functional/test_suites/observability/cypress/.eslintrc.json new file mode 100644 index 0000000000000..22a4d052afdc5 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "plugins": ["cypress"], + "extends": [ + "plugin:cypress/recommended" + ], + "env": { + "cypress/globals": true + }, + "rules": { + "cypress/no-force": "warn", + "import/no-extraneous-dependencies": "off" + } +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts new file mode 100644 index 0000000000000..ef5e7aebd7212 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/config_headless.ts @@ -0,0 +1,19 @@ +/* + * 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 { ObservabilityHeadlessTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const svlConfig = await readConfigFile(require.resolve('./oblt_config.base.ts')); + + return { + ...svlConfig.getAll(), + testRunner: ObservabilityHeadlessTestRunner, + }; +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/config_runner.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/config_runner.ts new file mode 100644 index 0000000000000..68bfdae6b9a91 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/config_runner.ts @@ -0,0 +1,19 @@ +/* + * 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 { ObservabilityTestRunner } from './runner'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const svlConfig = await readConfigFile(require.resolve('./oblt_config.base.ts')); + + return { + ...svlConfig.getAll(), + testRunner: ObservabilityTestRunner, + }; +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/cypress.config.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/cypress.config.ts new file mode 100644 index 0000000000000..f078275aab2eb --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/cypress.config.ts @@ -0,0 +1,33 @@ +/* + * 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, + videoUploadOnPasses: false, + screenshotOnRunFailure: false, + retries: { + runMode: 1, + }, + e2e: { + baseUrl: 'http://localhost:5620', + supportFile: './support/e2e.ts', + specPattern: './e2e/**/*.cy.ts', + }, +}); diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/e2e/navigation.cy.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/e2e/navigation.cy.ts new file mode 100644 index 0000000000000..83c1d85441c6e --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/e2e/navigation.cy.ts @@ -0,0 +1,104 @@ +/* + * 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('Serverless', () => { + it('Should login', () => { + cy.loginAsElasticUser(); + }); + + it('contains the side navigation for observabilitity serverless', () => { + cy.loginAsElasticUser(); + cy.contains('Discover'); + cy.contains('Dashboards'); + cy.contains('Alerts'); + cy.contains('AIOps'); + cy.contains('Applications'); + cy.contains('Cases'); + cy.contains('Visualizations'); + cy.contains('Add data'); + }); + + it('navigates to discover-dashboard-viz links', () => { + cy.loginAsElasticUser(); + + cy.contains('Discover').click(); + cy.url().should('include', '/app/discover'); + + cy.contains('Dashboards').click(); + cy.url().should('include', '/app/dashboards'); + + cy.contains('Visualizations').click(); + cy.url().should('include', '/app/visualize'); + }); + + it('navigates to alerts links', () => { + cy.loginAsElasticUser(); + + cy.contains('Alerts').click(); + cy.url().should('include', '/observability/alerts'); + + cy.contains('Cases').click(); + cy.url().should('include', '/observability/cases'); + + cy.contains('SLOs').click(); + cy.url().should('include', '/observability/slos'); + }); + + it('navigates to apm links', () => { + cy.loginAsElasticUser(); + + cy.contains('Applications').click(); + cy.contains('Services').click(); + cy.url().should('include', '/apm/services'); + + cy.contains('Traces').click(); + cy.url().should('include', '/apm/traces'); + + cy.contains('Dependencies').click(); + cy.url().should('include', '/apm/dependencies/inventory'); + }); + + it('navigates to get started links', () => { + cy.loginAsElasticUser(); + + cy.contains('Add data').click(); + cy.url().should('include', '/app/observabilityOnboarding'); + }); + + it('navigates to AIOps links', () => { + cy.loginAsElasticUser(); + + cy.contains('AIOps').click(); + cy.contains('Anomaly detection').click(); + cy.url().should('include', '/app/ml/jobs'); + + cy.contains('Spike analysis').click(); + cy.url().should('include', 'app/ml/aiops/explain_log_rate_spikes_index_select'); + + cy.contains('Change Point Detection').click(); + cy.url().should('include', 'app/ml/aiops/change_point_detection_index_select'); + + cy.contains('Job notifications').click(); + cy.url().should('include', 'app/ml/notifications'); + }); + + it('navigates to project settings', () => { + cy.loginAsElasticUser(); + + cy.contains('Project settings').click(); + cy.contains('Management').click(); + cy.url().should('include', '/app/management'); + + cy.contains('Integrations').click(); + cy.url().should('include', '/app/integrations/browse'); + + cy.contains('Fleet').click(); + cy.url().should('include', '/app/fleet/agents'); + }); +}); + +export {}; diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/oblt_config.base.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/oblt_config.base.ts new file mode 100644 index 0000000000000..22bd611067f48 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/oblt_config.base.ts @@ -0,0 +1,34 @@ +/* + * 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'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const svlSharedConfig = await readConfigFile( + require.resolve('../../../../shared/config.base.ts') + ); + + return { + ...svlSharedConfig.getAll(), + esTestCluster: { + ...svlSharedConfig.get('esTestCluster'), + serverArgs: [ + ...svlSharedConfig.get('esTestCluster.serverArgs'), + 'xpack.security.enabled=true', + ], + }, + kbnTestServer: { + ...svlSharedConfig.get('kbnTestServer'), + serverArgs: [ + ...svlSharedConfig.get('kbnTestServer.serverArgs'), + '--csp.strict=false', + '--csp.warnLegacyBrowsers=false', + '--serverless=oblt', + ], + }, + }; +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/package.json b/x-pack/test_serverless/functional/test_suites/observability/cypress/package.json new file mode 100644 index 0000000000000..3c2d296e313e2 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/package.json @@ -0,0 +1,13 @@ +{ + "author": "Elastic", + "name": "@kbn/serverless-observability", + "version": "1.0.0", + "private": true, + "license": "Elastic License 2.0", + "scripts": { + "cypress:open": "../../../../../../node_modules/.bin/cypress open --config-file ./cypress.config.ts", + "cypress:run": "../../../../../../node_modules/.bin/cypress run --browser chrome --config-file ./cypress.config.ts", + "cypress:serverless:open": "node ../../../../../../scripts/functional_tests --config ./config_runner.ts", + "cypress:serverless:run": "node ../../../../../../scripts/functional_tests --config ./config_server.ts" + } +} \ No newline at end of file diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/runner.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/runner.ts new file mode 100644 index 0000000000000..58895831b8a60 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/runner.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { resolve } from 'path'; +import { withProcRunner } from '@kbn/dev-proc-runner'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export type { FtrProviderContext } from '../../../ftr_provider_context'; + +export async function ObservabilityCypressTestRunner( + { getService }: FtrProviderContext, + command: string +) { + const log = getService('log'); + + await withProcRunner(log, async (procs) => { + await procs.run('cypress', { + cmd: 'yarn', + args: [command], + cwd: resolve(__dirname), + env: { + ...process.env, + }, + wait: true, + }); + }); +} + +export async function ObservabilityTestRunner(context: FtrProviderContext) { + return ObservabilityCypressTestRunner(context, 'cypress:open'); +} + +export async function ObservabilityHeadlessTestRunner(context: FtrProviderContext) { + return ObservabilityCypressTestRunner(context, 'cypress:run'); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/support/commands.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/support/commands.ts new file mode 100644 index 0000000000000..554fa134f0b38 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/support/commands.ts @@ -0,0 +1,21 @@ +/* + * 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-real-events/support'; +import 'cypress-axe'; + +Cypress.Commands.add('loginAsElasticUser', () => { + cy.visit('/', { + auth: { + username: 'elastic', + password: 'changeme', + }, + }); +}); + +Cypress.Commands.add('getByTestSubj', (selector: string) => { + return cy.get(`[data-test-subj="${selector}"]`); +}); diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/support/e2e.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/support/e2e.ts new file mode 100644 index 0000000000000..5f5d1eb3b3614 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/support/e2e.ts @@ -0,0 +1,12 @@ +/* + * 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. + */ + +Cypress.on('uncaught:exception', (err, runnable) => { + return false; +}); + +import './commands'; diff --git a/x-pack/test_serverless/functional/test_suites/observability/cypress/support/types.d.ts b/x-pack/test_serverless/functional/test_suites/observability/cypress/support/types.d.ts new file mode 100644 index 0000000000000..661545ba0c376 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/cypress/support/types.d.ts @@ -0,0 +1,13 @@ +/* + * 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 { + loginAsElasticUser(): Cypress.Chainable>; + getByTestSubj(selector: string): Chainable>; + } +}