diff --git a/.buildkite/scripts/steps/functional/scout_ui_tests.sh b/.buildkite/scripts/steps/functional/scout_ui_tests.sh index 57e463ecf9e74..db7d1f2637a34 100755 --- a/.buildkite/scripts/steps/functional/scout_ui_tests.sh +++ b/.buildkite/scripts/steps/functional/scout_ui_tests.sh @@ -33,7 +33,9 @@ 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" + run_tests "Observability Onboarding: Parallel Workers" "x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel.playwright.config.ts" "$run_mode" + # Disabled while we don't have any tests under the config + # run_tests "Observability Onboarding" "x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts" "$run_mode" done diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f9ac115415b4..48b91da711023 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -918,6 +918,7 @@ x-pack/solutions/observability/packages/get_padded_alert_time_range_util @elasti x-pack/solutions/observability/packages/kbn-alerts-grouping @elastic/response-ops x-pack/solutions/observability/packages/kbn-custom-integrations @elastic/obs-ux-logs-team x-pack/solutions/observability/packages/kbn-investigation-shared @elastic/obs-ux-management-team +x-pack/solutions/observability/packages/kbn-scout-oblt @elastic/appex-qa x-pack/solutions/observability/packages/kbn-streams-schema @elastic/streams-program-team x-pack/solutions/observability/packages/observability_ai/observability_ai_common @elastic/obs-ai-assistant x-pack/solutions/observability/packages/observability_ai/observability_ai_server @elastic/obs-ai-assistant diff --git a/package.json b/package.json index 3e0278f670b99..aa733e7f50a97 100644 --- a/package.json +++ b/package.json @@ -1488,6 +1488,7 @@ "@kbn/reporting-mocks-server": "link:src/platform/packages/private/kbn-reporting/mocks_server", "@kbn/scout": "link:packages/kbn-scout", "@kbn/scout-info": "link:packages/kbn-scout-info", + "@kbn/scout-oblt": "link:x-pack/solutions/observability/packages/kbn-scout-oblt", "@kbn/scout-reporting": "link:packages/kbn-scout-reporting", "@kbn/security-api-integration-helpers": "link:x-pack/test/security_api_integration/packages/helpers", "@kbn/serverless-storybook-config": "link:packages/serverless/storybook/config", diff --git a/packages/kbn-scout/index.ts b/packages/kbn-scout/index.ts index a7803d5e7104b..0b8fdad7e09c1 100644 --- a/packages/kbn-scout/index.ts +++ b/packages/kbn-scout/index.ts @@ -36,3 +36,8 @@ export type { ScoutServerConfig, ScoutTestConfig, } from './src/types'; + +// re-export from Playwright +export type { Locator } from 'playwright/test'; + +export { measurePerformance, measurePerformanceAsync } from './src/common'; diff --git a/packages/kbn-scout/src/playwright/fixtures/parallel_run_fixtures.ts b/packages/kbn-scout/src/playwright/fixtures/parallel_run_fixtures.ts index 854a3696ce183..7638b0eb91a2c 100644 --- a/packages/kbn-scout/src/playwright/fixtures/parallel_run_fixtures.ts +++ b/packages/kbn-scout/src/playwright/fixtures/parallel_run_fixtures.ts @@ -8,8 +8,9 @@ */ import { mergeTests } from 'playwright/test'; -import { coreWorkerFixtures, scoutSpaceParallelFixture } from './worker'; +import { apiFixtures, coreWorkerFixtures, scoutSpaceParallelFixture } from './worker'; import type { + ApiParallelWorkerFixtures, EsClient, KbnClient, KibanaUrl, @@ -29,6 +30,7 @@ export const scoutParallelFixtures = mergeTests( // worker scope fixtures coreWorkerFixtures, scoutSpaceParallelFixture, + apiFixtures, // test scope fixtures browserAuthFixture, scoutPageParallelFixture, @@ -42,7 +44,7 @@ export interface ScoutParallelTestFixtures { pageObjects: PageObjects; } -export interface ScoutParallelWorkerFixtures { +export interface ScoutParallelWorkerFixtures extends ApiParallelWorkerFixtures { log: ScoutLogger; config: ScoutTestConfig; kbnUrl: KibanaUrl; diff --git a/packages/kbn-scout/src/playwright/fixtures/single_thread_fixtures.ts b/packages/kbn-scout/src/playwright/fixtures/single_thread_fixtures.ts index 541695bc3ce7d..931f6e6f4d7c5 100644 --- a/packages/kbn-scout/src/playwright/fixtures/single_thread_fixtures.ts +++ b/packages/kbn-scout/src/playwright/fixtures/single_thread_fixtures.ts @@ -8,7 +8,13 @@ */ import { mergeTests } from 'playwright/test'; -import { coreWorkerFixtures, esArchiverFixture, uiSettingsFixture } from './worker'; +import { + ApiFixtures, + apiFixtures, + coreWorkerFixtures, + esArchiverFixture, + uiSettingsFixture, +} from './worker'; import type { EsArchiverFixture, EsClient, @@ -34,6 +40,8 @@ export const scoutFixtures = mergeTests( coreWorkerFixtures, esArchiverFixture, uiSettingsFixture, + // api fixtures + apiFixtures, // test scope fixtures browserAuthFixture, scoutPageFixture, @@ -47,7 +55,7 @@ export interface ScoutTestFixtures { pageObjects: PageObjects; } -export interface ScoutWorkerFixtures { +export interface ScoutWorkerFixtures extends ApiFixtures { log: ScoutLogger; config: ScoutTestConfig; kbnUrl: KibanaUrl; diff --git a/packages/kbn-scout/src/playwright/fixtures/test/scout_page/index.ts b/packages/kbn-scout/src/playwright/fixtures/test/scout_page/index.ts index b0486f961cc2a..7ea3887ccf826 100644 --- a/packages/kbn-scout/src/playwright/fixtures/test/scout_page/index.ts +++ b/packages/kbn-scout/src/playwright/fixtures/test/scout_page/index.ts @@ -8,6 +8,7 @@ */ import { Page } from '@playwright/test'; +import { PathOptions } from '../../../../common/services/kibana_url'; /** * Extends the Playwright 'Page' interface with methods specific to Kibana. @@ -24,7 +25,7 @@ export type ScoutPage = Page & { * @param options - Additional navigation options, passed directly to Playwright's `goto` method. * @returns A Promise resolving to a Playwright `Response` or `null`. */ - gotoApp: (appName: string, options?: Parameters[1]) => ReturnType; + gotoApp: (appName: string, pathOptions?: PathOptions) => ReturnType; /** * Waits for the Kibana loading spinner indicator to disappear. * @returns A Promise resolving when the indicator is hidden. diff --git a/packages/kbn-scout/src/playwright/fixtures/test/scout_page/parallel.ts b/packages/kbn-scout/src/playwright/fixtures/test/scout_page/parallel.ts index 8db44ef77c702..baceb3defbfe9 100644 --- a/packages/kbn-scout/src/playwright/fixtures/test/scout_page/parallel.ts +++ b/packages/kbn-scout/src/playwright/fixtures/test/scout_page/parallel.ts @@ -8,6 +8,7 @@ */ import { Page, test as base } from '@playwright/test'; +import { PathOptions } from '../../../../common/services/kibana_url'; import { ScoutPage } from '.'; import { KibanaUrl, ScoutLogger } from '../../worker'; import { ScoutSpaceParallelFixture } from '../../worker/scout_space'; @@ -29,8 +30,8 @@ export const scoutPageParallelFixture = base.extend< const extendedPage = extendPlaywrightPage({ page, kbnUrl }); // Overriding navigation to specific Kibana apps: url should respect the Kibana Space id - extendedPage.gotoApp = (appName: string) => - page.goto(kbnUrl.app(appName, { space: scoutSpace.id })); + extendedPage.gotoApp = (appName: string, pathOptions?: PathOptions) => + page.goto(kbnUrl.app(appName, { space: scoutSpace.id, pathOptions })); log.serviceLoaded(`scoutPage:${scoutSpace.id}`); await use(extendedPage); diff --git a/packages/kbn-scout/src/playwright/fixtures/test/scout_page/single_thread.ts b/packages/kbn-scout/src/playwright/fixtures/test/scout_page/single_thread.ts index 239498a9554da..12a50ee925815 100644 --- a/packages/kbn-scout/src/playwright/fixtures/test/scout_page/single_thread.ts +++ b/packages/kbn-scout/src/playwright/fixtures/test/scout_page/single_thread.ts @@ -9,6 +9,7 @@ import { Page } from '@playwright/test'; import { subj } from '@kbn/test-subj-selector'; +import { PathOptions } from '../../../../common/services/kibana_url'; import { KibanaUrl, ScoutLogger, coreWorkerFixtures } from '../../worker'; import { ScoutPage } from '.'; @@ -81,7 +82,8 @@ export function extendPlaywrightPage({ // Extend page with '@kbn/test-subj-selector' support extendedPage.testSubj = extendPageWithTestSubject(page); // Method to navigate to specific Kibana apps - extendedPage.gotoApp = (appName: string) => page.goto(kbnUrl.app(appName)); + extendedPage.gotoApp = (appName: string, pathOptions?: PathOptions) => + page.goto(kbnUrl.app(appName, { pathOptions })); // Method to wait for global loading indicator to be hidden extendedPage.waitForLoadingIndicatorHidden = () => extendedPage.testSubj.waitForSelector('globalLoadingIndicator-hidden', { diff --git a/packages/kbn-scout/src/playwright/fixtures/worker/apis/fleet/index.ts b/packages/kbn-scout/src/playwright/fixtures/worker/apis/fleet/index.ts new file mode 100644 index 0000000000000..b645cf1933673 --- /dev/null +++ b/packages/kbn-scout/src/playwright/fixtures/worker/apis/fleet/index.ts @@ -0,0 +1,71 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { measurePerformanceAsync } from '../../../../../common'; +import { coreWorkerFixtures } from '../../core_fixtures'; + +export interface FleetApiFixture { + integration: { + install: (name: string) => Promise; + delete: (name: string) => Promise; + }; +} + +/** + * This fixture provides a helper to interact with the Fleet API. + */ +export const fleetApiFixture = coreWorkerFixtures.extend<{}, { fleetApi: FleetApiFixture }>({ + fleetApi: [ + async ({ kbnClient, log }, use) => { + const fleetApiHelper = { + integration: { + install: async (name: string) => { + await measurePerformanceAsync( + log, + `fleetApi.integration.install [${name}]`, + async () => { + await 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' }, + ], + }, + }); + } + ); + }, + + delete: async (name: string) => { + await measurePerformanceAsync( + log, + `fleetApi.integration.delete [${name}]`, + async () => { + await kbnClient.request({ + method: 'DELETE', + path: `/api/fleet/epm/packages/${name}`, + ignoreErrors: [400], + }); + } + ); + }, + }, + }; + + log.serviceLoaded('fleetApi'); + await use(fleetApiHelper); + }, + { scope: 'worker' }, + ], +}); diff --git a/packages/kbn-scout/src/playwright/fixtures/worker/apis/index.ts b/packages/kbn-scout/src/playwright/fixtures/worker/apis/index.ts new file mode 100644 index 0000000000000..ab3b50a2e4bd4 --- /dev/null +++ b/packages/kbn-scout/src/playwright/fixtures/worker/apis/index.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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { mergeTests } from 'playwright/test'; +import { FleetApiFixture, fleetApiFixture } from './fleet'; + +export const apiFixtures = mergeTests(fleetApiFixture); + +export interface ApiFixtures { + fleetApi: FleetApiFixture; +} + +export interface ApiParallelWorkerFixtures { + fleetApi: FleetApiFixture; +} diff --git a/packages/kbn-scout/src/playwright/fixtures/worker/index.ts b/packages/kbn-scout/src/playwright/fixtures/worker/index.ts index f6ca4561eaa07..ab4a966ee9199 100644 --- a/packages/kbn-scout/src/playwright/fixtures/worker/index.ts +++ b/packages/kbn-scout/src/playwright/fixtures/worker/index.ts @@ -25,3 +25,6 @@ export type { UiSettingsFixture } from './ui_settings'; export { scoutSpaceParallelFixture } from './scout_space'; export type { ScoutSpaceParallelFixture } from './scout_space'; + +export { apiFixtures } from './apis'; +export type { ApiFixtures, ApiParallelWorkerFixtures } from './apis'; diff --git a/tsconfig.base.json b/tsconfig.base.json index bcc88b1a5bc41..a070ce01350b0 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1566,6 +1566,8 @@ "@kbn/scout/*": ["packages/kbn-scout/*"], "@kbn/scout-info": ["packages/kbn-scout-info"], "@kbn/scout-info/*": ["packages/kbn-scout-info/*"], + "@kbn/scout-oblt": ["x-pack/solutions/observability/packages/kbn-scout-oblt"], + "@kbn/scout-oblt/*": ["x-pack/solutions/observability/packages/kbn-scout-oblt/*"], "@kbn/scout-reporting": ["packages/kbn-scout-reporting"], "@kbn/scout-reporting/*": ["packages/kbn-scout-reporting/*"], "@kbn/screenshot-mode-example-plugin": ["examples/screenshot_mode_example"], diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/README.md b/x-pack/solutions/observability/packages/kbn-scout-oblt/README.md new file mode 100644 index 0000000000000..5da551151c71e --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/README.md @@ -0,0 +1,45 @@ +# @kbn/scout-oblt + +`@kbn/scout-oblt` is a test library that extends `@kbn/scout` with test helpers specifically designed for `Observability` products in Kibana. + +Its primary goal is to simplify the test development experience for teams working on `Observability` plugins by providing custom Playwright fixtures, page objects, and utilities tailored for Observability-related testing scenarios. + +### Table of Contents + +1. Folder Structure +2. How to Use +3. Contributing + +### Folder Structure + +The `@kbn/scout-oblt` structure includes the following key directories and files: + +``` +x-pack/solutions/observability/packages/kbn-scout-oblt/ +├── src/ +│ ├── playwright/ +│ │ └── fixtures +│ │ │ └── test/ +│ │ │ │ └── // Observability test-scope fixtures +│ │ │ └── worker/ +│ │ │ │ └── // Observability worker-scope fixtures +│ │ │ └── single_thread_fixtures.ts +│ │ │ └── parallel_run_fixtures.ts +│ │ │ └── index.ts +│ │ └── page_objects/ +│ │ │ └── // Observability pages +│ └── index.ts +├── package.json +├── tsconfig.json +``` + +### How to use + +``` +import { test } from '@kbn/scout-oblt'; + +test('verifies Observability Home loads', async ({ page, pageObjects }) => { + await pageObjects.onboardingHome.goto(); + expect(await page.title()).toContain('Observability'); +}); +``` diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/index.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/index.ts new file mode 100644 index 0000000000000..804af1d20f7ab --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/index.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { test, spaceTest } from './src/playwright'; +export type { + ObltPageObjects, + ObltTestFixtures, + ObltWorkerFixtures, + ObltParallelTestFixtures, + ObltParallelWorkerFixtures, +} from './src/playwright'; + +// re-export from @kbn/scout +export { + expect, + tags, + createPlaywrightConfig, + createLazyPageObject, + ingestTestDataHook, +} from '@kbn/scout'; + +export type { + EsClient, + KbnClient, + KibanaUrl, + ScoutLogger, + ScoutPage, + PageObjects, + ScoutServerConfig, + ScoutTestConfig, + ScoutPlaywrightOptions, + ScoutTestOptions, + Locator, +} from '@kbn/scout'; diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/jest.config.js b/x-pack/solutions/observability/packages/kbn-scout-oblt/jest.config.js new file mode 100644 index 0000000000000..00a75697f0624 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../../../..', + roots: ['/x-pack/solutions/observability/packages/kbn-scout-oblt'], +}; diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/kibana.jsonc b/x-pack/solutions/observability/packages/kbn-scout-oblt/kibana.jsonc new file mode 100644 index 0000000000000..76ad3d24c94b1 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/kibana.jsonc @@ -0,0 +1,6 @@ +{ + "type": "test-helper", + "id": "@kbn/scout-oblt", + "owner": "@elastic/appex-qa", + "devOnly": true +} diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/package.json b/x-pack/solutions/observability/packages/kbn-scout-oblt/package.json new file mode 100644 index 0000000000000..5da158d710001 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/scout-oblt", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/index.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/index.ts new file mode 100644 index 0000000000000..b05e86d128a2e --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { test } from './single_thread_fixtures'; +export { spaceTest } from './parallel_run_fixtures'; +export * from './types'; diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/parallel_run_fixtures.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/parallel_run_fixtures.ts new file mode 100644 index 0000000000000..09501b5231749 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/parallel_run_fixtures.ts @@ -0,0 +1,30 @@ +/* + * 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 { spaceTest as spaceBase } from '@kbn/scout'; + +import { extendPageObjects } from '../page_objects'; +import { ObltParallelTestFixtures, ObltParallelWorkerFixtures } from './types'; + +/** + * Should be used test spec files, running in parallel in isolated spaces agaist the same Kibana instance. + */ +export const spaceTest = spaceBase.extend({ + pageObjects: async ( + { + pageObjects, + page, + }: { + pageObjects: ObltParallelTestFixtures['pageObjects']; + page: ObltParallelTestFixtures['page']; + }, + use: (pageObjects: ObltParallelTestFixtures['pageObjects']) => Promise + ) => { + const extendedPageObjects = extendPageObjects(pageObjects, page); + await use(extendedPageObjects); + }, +}); diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/single_thread_fixtures.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/single_thread_fixtures.ts new file mode 100644 index 0000000000000..24497b4f414be --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/single_thread_fixtures.ts @@ -0,0 +1,30 @@ +/* + * 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 } from '@kbn/scout'; + +import { extendPageObjects } from '../page_objects'; +import { ObltTestFixtures, ObltWorkerFixtures } from './types'; + +/** + * Should be used for the test spec files executed seqentially. + */ +export const test = base.extend({ + pageObjects: async ( + { + pageObjects, + page, + }: { + pageObjects: ObltTestFixtures['pageObjects']; + page: ObltTestFixtures['page']; + }, + use: (pageObjects: ObltTestFixtures['pageObjects']) => Promise + ) => { + const extendedPageObjects = extendPageObjects(pageObjects, page); + await use(extendedPageObjects); + }, +}); diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/types.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/types.ts new file mode 100644 index 0000000000000..ca432dfb4820f --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/fixtures/types.ts @@ -0,0 +1,26 @@ +/* + * 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 { + ScoutParallelTestFixtures, + ScoutParallelWorkerFixtures, + ScoutTestFixtures, + ScoutWorkerFixtures, +} from '@kbn/scout'; +import { ObltPageObjects } from '../page_objects'; + +export interface ObltTestFixtures extends ScoutTestFixtures { + pageObjects: ObltPageObjects; +} + +export type ObltWorkerFixtures = ScoutWorkerFixtures; + +export interface ObltParallelTestFixtures extends ScoutParallelTestFixtures { + pageObjects: ObltPageObjects; +} + +export type ObltParallelWorkerFixtures = ScoutParallelWorkerFixtures; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/index.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/index.ts similarity index 75% rename from x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/index.ts rename to x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/index.ts index c032371659593..6547510ec58a9 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/index.ts +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { OnboardingHomePage } from './onboarding_home'; +export * from './fixtures'; +export type { ObltPageObjects } from './page_objects'; diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/custom_logs.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/custom_logs.ts new file mode 100644 index 0000000000000..6001bf086fed3 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/custom_logs.ts @@ -0,0 +1,125 @@ +/* + * 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, Locator } from '@kbn/scout'; + +export class CustomLogsPage { + public advancedSettingsContent: Locator; + public logFilePathList: Locator; + public addLogFilePathButton: Locator; + public integrationNameInput: Locator; + public datasetNameInput: Locator; + public serviceNameInput: Locator; + public namespaceInput: Locator; + public customConfigInput: Locator; + public customIntegrationInstalledCallout: Locator; + public customIntegrationErrorCallout: Locator; + public apiKeyCreatedCallout: Locator; + public apiKeyPrivilegesErrorCallout: Locator; + public apiKeyCreateErrorCallout: Locator; + public autoDownloadConfigurationToggle: Locator; + public autoDownloadConfigurationCallout: Locator; + public installCodeSnippet: Locator; + public windowsInstallElasticAgentDocLink: Locator; + public configureElasticAgentStep: Locator; + public downloadConfigurationButton: Locator; + public continueButton: Locator; + public exploreLogsButton: Locator; + public checkLogsStepMessage: Locator; + + constructor(private readonly page: ScoutPage) { + this.advancedSettingsContent = this.page.testSubj + .locator('obltOnboardingCustomLogsAdvancedSettings') + .getByRole('group'); + this.logFilePathList = this.page.locator(`[data-test-subj^=obltOnboardingLogFilePath-]`); + 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.continueButton = page.testSubj.locator('obltOnboardingCustomLogsContinue'); + + this.customConfigInput = this.page.testSubj.locator('obltOnboardingCustomLogsCustomConfig'); + this.customIntegrationInstalledCallout = this.page.testSubj.locator( + 'obltOnboardingCustomIntegrationInstalled' + ); + this.customIntegrationErrorCallout = this.page.testSubj.locator( + 'obltOnboardingCustomIntegrationErrorCallout' + ); + this.apiKeyCreatedCallout = this.page.testSubj.locator('obltOnboardingLogsApiKeyCreated'); + this.apiKeyPrivilegesErrorCallout = this.page.testSubj.locator( + 'obltOnboardingLogsApiKeyCreationNoPrivileges' + ); + this.apiKeyCreateErrorCallout = this.page.testSubj.locator( + 'obltOnboardingLogsApiKeyCreationFailed' + ); + 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.exploreLogsButton = this.page.testSubj.locator('obltOnboardingExploreLogs'); + this.checkLogsStepMessage = this.page.testSubj + .locator('obltOnboardingCheckLogsStep') + .locator(`.euiStep__title`); + } + + async goto() { + return this.page.gotoApp('observabilityOnboarding/customLogs'); + } + + async clickBackButton() { + return this.page.testSubj.click('observabilityOnboardingFlowBackToSelectionButton'); + } + + getLogFilePathInputField(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(); + } + + getStepStatusLocator(status: 'loading' | 'complete' | 'danger' | 'warning') { + return this.page.testSubj.locator(`obltOnboardingStepStatus-${status}`); + } + + async selectPlatform(name: 'linux' | 'macos' | 'windows') { + return this.page.testSubj.click(name); + } + + getCheckLogsStepLocator(status: 'loading' | 'incomplete' | 'complete') { + return this.page.testSubj + .locator('obltOnboardingCheckLogsStep') + .locator(`.euiStep__titleWrapper [class$="euiStepNumber-s-${status}"]`); + } +} diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/index.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/index.ts new file mode 100644 index 0000000000000..b77086fc337a6 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { PageObjects, ScoutPage, createLazyPageObject } from '@kbn/scout'; +import { OnboardingHomePage } from './onboarding_home'; +import { CustomLogsPage } from './custom_logs'; + +export interface ObltPageObjects extends PageObjects { + onboardingHome: OnboardingHomePage; + customLogs: CustomLogsPage; +} + +export function extendPageObjects(pageObjects: PageObjects, page: ScoutPage): ObltPageObjects { + return { + ...pageObjects, + onboardingHome: createLazyPageObject(OnboardingHomePage, page), + customLogs: createLazyPageObject(CustomLogsPage, page), + }; +} diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/onboarding_home.ts b/x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/onboarding_home.ts similarity index 100% rename from x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/onboarding_home.ts rename to x-pack/solutions/observability/packages/kbn-scout-oblt/src/playwright/page_objects/onboarding_home.ts diff --git a/x-pack/solutions/observability/packages/kbn-scout-oblt/tsconfig.json b/x-pack/solutions/observability/packages/kbn-scout-oblt/tsconfig.json new file mode 100644 index 0000000000000..3e64371580f26 --- /dev/null +++ b/x-pack/solutions/observability/packages/kbn-scout-oblt/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/scout", + ] +} 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 index 9a45d498a61eb..ccf77c804d3c1 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/README.md +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/README.md @@ -12,6 +12,8 @@ node scripts/scout.js start-server --serverless=[es|oblt|security] Then you can run the tests in another terminal: +Some tests are designed to run sequentially: + ```bash // ESS npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts --grep @ess @@ -20,4 +22,14 @@ npx playwright test --config x-pack/solutions/observability/plugins/observabilit npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts --grep @svlOblt ``` +Some tests are designed to run concurrently (preferred option): + +```bash +// ESS +npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_playwright.config.ts --grep @ess + +// Serverless +npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_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/assertion_messages.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/assertion_messages.ts new file mode 100644 index 0000000000000..9f3dd7a9437e7 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/assertion_messages.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const FIELD_VALIDATION = { + INTEGRATION_NAME_LOWERCASE: 'An integration name should be lowercase.', + DATASET_NAME_LOWERCASE: 'A dataset name should be lowercase.', +}; 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 index a928c1d6fe34b..c14be56108e85 100644 --- 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 @@ -5,46 +5,35 @@ * 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'; +import { v4 as uuidv4 } from 'uuid'; +import { test as base, ObltTestFixtures, ObltWorkerFixtures } from '@kbn/scout-oblt'; +import { mergeTests } from 'playwright/test'; +import { onboardingApiFixture, OnboardingApiFixture } from './onboarding_api'; -export interface ExtendedScoutTestFixtures extends ScoutTestFixtures { - pageObjects: PageObjects & { - onboardingHomePage: OnboardingHomePage; - customLogsPage: CustomLogsPage; - }; +export type ExtendedScoutTestFixtures = ObltTestFixtures; +export interface ExtendedScoutWorkerFixtures extends ObltWorkerFixtures { + onboardingApi: OnboardingApiFixture; } -export const test = base.extend({ +const testFixtures = mergeTests(base, onboardingApiFixture); + +export const test = testFixtures.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); }, }); + +export const generateIntegrationName = (name: string) => `${name}_${uuidv4().slice(0, 5)}`; + +export * as assertionMessages from './assertion_messages'; diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/onboarding_api.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/onboarding_api.ts new file mode 100644 index 0000000000000..9309229f1fd1c --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/onboarding_api.ts @@ -0,0 +1,48 @@ +/* + * 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 } from '@kbn/scout-oblt'; + +export interface OnboardingApiFixture { + updateInstallationStepStatus: ( + onboardingId: string, + step: string, + status: string, + payload?: object + ) => Promise; +} + +/** + * This fixture provides a helper to interact with the Observability Onboarding API. + */ +export const onboardingApiFixture = base.extend<{}, { onboardingApi: OnboardingApiFixture }>({ + onboardingApi: [ + async ({ kbnClient, log }, use) => { + const onboardingApiHelper: OnboardingApiFixture = { + updateInstallationStepStatus: async ( + onboardingId: string, + step: string, + status: string, + payload?: object + ) => { + await kbnClient.request({ + method: 'POST', + path: `/internal/observability_onboarding/flow/${onboardingId}/step/${step}`, + body: { + status, + payload, + }, + }); + }, + }; + + log.serviceLoaded('onboardingApi'); + await use(onboardingApiHelper); + }, + { scope: 'worker' }, + ], +}); 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 deleted file mode 100644 index 1b2b8119e5ce2..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/fixtures/page_objects/custom_logs.ts +++ /dev/null @@ -1,195 +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 { 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/ui_tests/parallel.playwright.config.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel.playwright.config.ts new file mode 100644 index 0000000000000..997f5d57098fa --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel.playwright.config.ts @@ -0,0 +1,14 @@ +/* + * 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 { createPlaywrightConfig } from '@kbn/scout'; + +// eslint-disable-next-line import/no-default-export +export default createPlaywrightConfig({ + testDir: './parallel_tests', + workers: 2, +}); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/add_custom_integration.spec.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/add_custom_integration.spec.ts new file mode 100644 index 0000000000000..ad22d72cbe34b --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/add_custom_integration.spec.ts @@ -0,0 +1,83 @@ +/* + * 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-oblt'; +import { generateIntegrationName, test } from '../../fixtures'; + +test.describe( + 'Observability Onboarding - Custom Integration', + { tag: ['@ess', '@svlOblt'] }, + () => { + const integrationName = generateIntegrationName('mylogs'); + const logsFilePath = `${integrationName}.log`; + + test.beforeEach(async ({ browserAuth, pageObjects: { customLogs } }) => { + await browserAuth.loginAsAdmin(); + await customLogs.goto(); + }); + + test.afterEach(async ({ fleetApi }) => { + await fleetApi.integration.delete(integrationName); + }); + + test('should be installed, show API Key and correct instructions', async ({ + pageObjects: { customLogs }, + page, + }) => { + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await expect(customLogs.integrationNameInput).toHaveValue(integrationName); + await expect(customLogs.datasetNameInput).toHaveValue(integrationName); + await expect(customLogs.serviceNameInput).toHaveValue(''); + + await customLogs.continueButton.click(); + await expect( + customLogs.customIntegrationInstalledCallout, + `'mylogs integration installed' should be displayed` + ).toBeVisible(); + + await expect( + customLogs.apiKeyCreatedCallout, + `'API Key created' should be displayed` + ).toBeVisible(); + + // validate default 'Install the Elastic Agent' instructions + await expect(page.testSubj.locator('linux-tar')).toHaveAttribute('aria-pressed', 'true'); + await expect(customLogs.autoDownloadConfigurationToggle).not.toBeChecked(); + await expect(customLogs.installCodeSnippet).toBeVisible(); + + await customLogs.selectPlatform('macos'); + await expect(customLogs.autoDownloadConfigurationToggle).not.toBeChecked(); + await expect(customLogs.installCodeSnippet).toBeVisible(); + + await customLogs.selectPlatform('windows'); + await expect(customLogs.autoDownloadConfigurationToggle).toBeDisabled(); + await expect(customLogs.windowsInstallElasticAgentDocLink).toBeVisible(); + await expect(customLogs.installCodeSnippet).not.toBeVisible(); + await expect( + customLogs.configureElasticAgentStep.getByText('Step 2 is disabled') + ).toBeVisible(); + }); + + test('should update instructions when automatic config download toggled', async ({ + pageObjects: { customLogs }, + }) => { + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await customLogs.continueButton.click(); + + await customLogs.autoDownloadConfigurationToggle.click(); + await expect(customLogs.autoDownloadConfigurationCallout).toBeVisible(); + await expect(customLogs.installCodeSnippet).toContainText('autoDownloadConfig=1'); + + await customLogs.selectPlatform('macos'); + await expect(customLogs.autoDownloadConfigurationCallout).toBeVisible(); + await expect(customLogs.installCodeSnippet).toContainText('autoDownloadConfig=1'); + + await customLogs.autoDownloadConfigurationToggle.click(); + await expect(customLogs.installCodeSnippet).not.toContainText('autoDownloadConfig=1'); + }); + } +); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/configuration.spec.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/configuration.spec.ts new file mode 100644 index 0000000000000..c3cd104b461a8 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/configuration.spec.ts @@ -0,0 +1,107 @@ +/* + * 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-oblt'; +import { assertionMessages, generateIntegrationName, test } from '../../fixtures'; + +test.describe( + 'Observability Onboarding - Custom logs configuration', + { tag: ['@ess', '@svlOblt'] }, + () => { + const logsFilePath = `${generateIntegrationName('mylogs')}.log`; + + test.beforeEach(async ({ browserAuth, pageObjects: { customLogs } }) => { + await browserAuth.loginAsAdmin(); + await customLogs.goto(); + }); + + test('should navigate to the onboarding home page when the back button clicked', async ({ + pageObjects: { customLogs }, + page, + }) => { + await customLogs.clickBackButton(); + expect(page.url()).toContain('/app/observabilityOnboarding'); + }); + + test(`should allow multiple entries for Log File Path`, async ({ + pageObjects: { customLogs }, + }) => { + await expect( + customLogs.getLogFilePathInputField(0), + 'Log File Path should be empty' + ).toHaveValue(''); + await expect( + customLogs.continueButton, + 'Continue button should be disabled when Log File Path is not set' + ).toBeDisabled(); + await customLogs.addLogFilePathButton.click(); + await expect(customLogs.logFilePathList).toHaveCount(2); + + await customLogs.logFilePathDeleteButton(1).click(); + await expect(customLogs.logFilePathList).toHaveCount(1); + }); + + test(`should allow updating Advanced Settings`, async ({ pageObjects: { customLogs } }) => { + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await expect(customLogs.advancedSettingsContent).not.toBeVisible(); + await customLogs.clickAdvancedSettingsButton(); + await expect( + customLogs.advancedSettingsContent, + 'Advanced Settings should be opened' + ).toBeVisible(); + await expect(customLogs.namespaceInput).toHaveValue('default'); + + await customLogs.namespaceInput.fill(''); + await expect( + customLogs.continueButton, + 'Continue button should be disabled when Namespace is empty' + ).toBeDisabled(); + + await customLogs.namespaceInput.fill('default'); + await expect(customLogs.customConfigInput).toHaveValue(''); + await expect(customLogs.continueButton).not.toBeDisabled(); + + await customLogs.clickAdvancedSettingsButton(); + await expect( + customLogs.advancedSettingsContent, + 'Advanced Settings should be closed' + ).not.toBeVisible(); + }); + + test('should validate Integration Name field', async ({ + pageObjects: { customLogs }, + page, + }) => { + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + + await customLogs.integrationNameInput.fill(''); + await expect(customLogs.continueButton).toBeDisabled(); + + await customLogs.integrationNameInput.fill('hello$world'); + await expect(customLogs.integrationNameInput).toHaveValue('hello_world'); + + await customLogs.integrationNameInput.fill('H3llowOrld'); + await expect( + page.getByText(assertionMessages.FIELD_VALIDATION.INTEGRATION_NAME_LOWERCASE) + ).toBeVisible(); + }); + + test('should validate DataSet Name field', async ({ pageObjects: { customLogs }, page }) => { + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await customLogs.datasetNameInput.fill(''); + await expect(customLogs.continueButton).toBeDisabled(); + + await customLogs.datasetNameInput.fill('hello$world'); + await expect(customLogs.datasetNameInput).toHaveValue('hello_world'); + + await customLogs.datasetNameInput.fill('H3llowOrld'); + await expect( + page.getByText(assertionMessages.FIELD_VALIDATION.DATASET_NAME_LOWERCASE) + ).toBeVisible(); + }); + } +); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/error_handling.spec.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/error_handling.spec.ts new file mode 100644 index 0000000000000..c056d8b8109f9 --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/error_handling.spec.ts @@ -0,0 +1,109 @@ +/* + * 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-oblt'; +import { generateIntegrationName, test } from '../../fixtures'; + +test.describe( + 'Observability Onboarding - Custom Integration Error', + { tag: ['@ess', '@svlOblt'] }, + () => { + const integrationName = generateIntegrationName('mylogs'); + const logsFilePath = `${integrationName}.log`; + + test.afterEach(async ({ fleetApi }) => { + await fleetApi.integration.delete(integrationName); + }); + + test('should be displayed when user has no previleges', async ({ + browserAuth, + pageObjects: { customLogs }, + }) => { + await browserAuth.loginAsPrivilegedUser(); + await customLogs.goto(); + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await customLogs.continueButton.click(); + await expect( + customLogs.customIntegrationInstalledCallout, + `'mylogs integration installed' should be displayed` + ).toBeVisible(); + + await expect( + customLogs.apiKeyPrivilegesErrorCallout, + `'User does not have permissions to create API key' should be displayed` + ).toBeVisible(); + }); + + test('should be displayed when API Key creation failed', async ({ + browserAuth, + pageObjects: { customLogs }, + page, + }) => { + await page.route('**/observability_onboarding/logs/flow', (route) => { + route.fulfill({ + status: 500, + body: JSON.stringify({ + message: 'Internal error', + }), + }); + }); + await browserAuth.loginAsAdmin(); + await customLogs.goto(); + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await customLogs.continueButton.click(); + await expect( + customLogs.customIntegrationInstalledCallout, + `'mylogs integration installed' should be displayed` + ).toBeVisible(); + + await expect( + customLogs.apiKeyCreateErrorCallout, + `'Failed to create API Key' should be displayed` + ).toBeVisible(); + }); + + test('should be displayed when integration failed', async ({ + browserAuth, + pageObjects: { customLogs }, + page, + kbnUrl, + }) => { + await page.route(kbnUrl.get('/api/fleet/epm/custom_integrations'), (route) => { + route.fulfill({ + status: 500, + json: { + message: 'Internal error', + }, + }); + }); + + await browserAuth.loginAsAdmin(); + await customLogs.goto(); + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await customLogs.continueButton.click(); + + await expect(customLogs.customIntegrationErrorCallout).toBeVisible(); + }); + + test('should be displayed when integration with the same name exists', async ({ + browserAuth, + pageObjects: { customLogs }, + fleetApi, + page, + }) => { + await fleetApi.integration.install(integrationName); + await browserAuth.loginAsAdmin(); + await customLogs.goto(); + + await customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await customLogs.continueButton.click(); + + const errorMessage = `Failed to create the integration as an installation with the name ${integrationName} already exists.`; + await expect(page.getByText(errorMessage)).toBeVisible(); + }); + } +); diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/install_elastic_agent.spec.ts b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/install_elastic_agent.spec.ts new file mode 100644 index 0000000000000..4f3ca2aae077c --- /dev/null +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel_tests/custom_logs/install_elastic_agent.spec.ts @@ -0,0 +1,181 @@ +/* + * 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, Locator } from '@kbn/scout-oblt'; +import { generateIntegrationName, test } from '../../fixtures'; + +const checkAgentStatusUpdated = async (locator: Locator, status: string) => + expect(locator.getByText(status)).toBeVisible(); + +test.describe('Observability Onboarding - Elastic Agent', { tag: ['@ess', '@svlOblt'] }, () => { + const integrationName = generateIntegrationName('mylogs'); + const logsFilePath = `${integrationName}.log`; + let onboardingId: string; + + test.beforeEach(async ({ browserAuth, pageObjects, page }) => { + 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 }); + }); + + // login and create custom integration + await browserAuth.loginAsAdmin(); + await pageObjects.customLogs.goto(); + await pageObjects.customLogs.getLogFilePathInputField(0).fill(logsFilePath); + await pageObjects.customLogs.continueButton.click(); + await pageObjects.customLogs.apiKeyCreatedCallout.waitFor({ state: 'visible' }); + }); + + test.afterEach(async ({ fleetApi }) => { + await fleetApi.integration.delete(integrationName); + }); + + test('should be installed sucessfully', async ({ + pageObjects: { customLogs }, + onboardingApi, + }) => { + // downloading agent + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-download', 'loading'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('loading'), + 'Downloading Elastic Agent' + ); + // downloading agent failed + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-download', 'danger'); + await expect(customLogs.getStepStatusLocator('danger'), 'Download Elastic Agent').toBeVisible(); + // downloading agent completed + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('complete'), + 'Elastic Agent downloaded' + ); + + // extracting agent + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-extract', 'loading'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('loading'), + 'Extracting Elastic Agent' + ); + // extracting agent completed + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('complete'), + 'Elastic Agent extracted' + ); + + // installing agent failed + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-install', 'danger'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('danger'), + 'Install Elastic Agent' + ); + // installing agent completed + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('complete'), + 'Elastic Agent installed' + ); + + // agent connecting + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('loading'), + 'Connecting to the Elastic Agent' + ); + await expect(customLogs.getCheckLogsStepLocator('incomplete')).toBeVisible(); + await expect(customLogs.checkLogsStepMessage).toHaveText('Ship logs to Elastic Observability'); + // agent connected + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { + agentId: 'test-agent-id', + }); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('complete'), + 'Connected to the Elastic Agent' + ); + }); + + test('should be configured sucessfully for Linux and wait for logs', async ({ + pageObjects: { customLogs }, + onboardingApi, + }) => { + // install and connect agent for linux + await customLogs.autoDownloadConfigurationToggle.click(); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { + agentId: 'test-agent-id', + }); + + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('loading'), + 'Downloading Elastic Agent config' + ); + + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('complete'), + 'Elastic Agent config written to /opt/Elastic/Agent/elastic-agent.yml' + ); + + await expect(customLogs.getCheckLogsStepLocator('loading')).toBeVisible(); + await expect(customLogs.checkLogsStepMessage).toHaveText('Waiting for logs to be shipped...'); + }); + + test('should be configured sucessfully for MacOS and wait for logs', async ({ + pageObjects: { customLogs }, + onboardingApi, + }) => { + // install and connect agent for macos + await customLogs.selectPlatform('macos'); + await customLogs.autoDownloadConfigurationToggle.click(); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete'); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete'); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete'); + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', { + agentId: 'test-agent-id', + }); + + await onboardingApi.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete'); + await checkAgentStatusUpdated( + customLogs.getStepStatusLocator('complete'), + 'Elastic Agent config written to /Library/Elastic/Agent/elastic-agent.yml' + ); + + await expect(customLogs.getCheckLogsStepLocator('loading')).toBeVisible(); + await expect(customLogs.checkLogsStepMessage).toHaveText('Waiting for logs to be shipped...'); + }); + + test('should ship logs to Elastic', async ({ page, pageObjects: { customLogs } }) => { + 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' }, + }, + }), + }); + }); + + await expect(customLogs.getCheckLogsStepLocator('complete')).toBeVisible(); + await expect(customLogs.checkLogsStepMessage).toHaveText('Logs are being shipped!'); + + await customLogs.exploreLogsButton.click(); + await expect(page).toHaveURL(/\/app\/discover/); + }); +}); 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 deleted file mode 100644 index 2a2db5af2c7ec..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/configuration.spec.ts +++ /dev/null @@ -1,256 +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 { 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 deleted file mode 100644 index 2dadb23090418..0000000000000 --- a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tests/custom_logs/install_elastic_agent.spec.ts +++ /dev/null @@ -1,696 +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 { 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 index 6f9ee7501fef3..f29bcbcf52ebf 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tsconfig.json +++ b/x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/tsconfig.json @@ -7,7 +7,8 @@ "**/*" ], "kbn_references": [ - "@kbn/scout" + "@kbn/scout", + "@kbn/scout-oblt" ], "exclude": [ "target/**/*" diff --git a/yarn.lock b/yarn.lock index 2629e5709ed65..b53889bac4e1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6977,6 +6977,10 @@ version "0.0.0" uid "" +"@kbn/scout-oblt@link:x-pack/solutions/observability/packages/kbn-scout-oblt": + version "0.0.0" + uid "" + "@kbn/scout-reporting@link:packages/kbn-scout-reporting": version "0.0.0" uid ""