mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[scout] adding test helper @kbn/scout-oblt
package and uptate onboarding tests (#209761)
## Summary `@kbn/scout-oblt` is a test library that extends `@kbn/scout` with test helpers specifically designed to test `Observability` applications in Kibana. All Oblt plugins should only import from `@kbn/scout-oblt` 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. Contributing: - when Fixture/Page Object is sharable across all Solutions and Platform (`fleetApi` fixture), it should be added in `@kbn/scout` - when Fixture/Page Object is Oblt-specific but is shared across tests under the multiple plugins (`OnboardingHome` page), it should be added in `@kbn/scout-oblt` - when Fixture/Page Object is only used in a single plugin (`onboarding` internal APIs ?), it should be added in this plugin. I also re-worked existing tests with few ideas in mind: - Scout is **e2e testing tool** and should target primary e2e test scenarios; We have _API integration tests_ to test multiple short scenarios for APIs behavior (response, status code) and _jest/React testing library_ to test components in isolation (elements rendering, fields validation). Doing all the testing with e2e tool like Playwright will dramatically affect cost efficiency and stability of tests, but also slows overall CI execution and PRs delivery. The goal is to follow testing pyramid and keep in mind its principles. - We on purpose spin up new browser context for each `test` block to make sure our **tests are independent**. Having too many short `test` blocks in the file significantly slows down the execution: every block triggers browser context, saml authentication, adding/removing Fleet integrations (each call up to 2 seconds) and other beforeEach/afterEach hooks. Real browser-based testing is expensive. It is not about putting every step into 1 `test` block, but also not a Jest unit-test-style design. When it is possible to group similar actions on the same page and if it is a part of the same user flow - we should do it. It also doesn't bring the testing value repeating the same UI steps multiple times in different scenarios. _Our CI costs are critical to cut when it is possible_ - Avoid **nesting describe** blocks: it complicates test readability and also complicates for CI bot to properly skip the failing block (it will skip the top level one). We encourage **Scout parallel test execution** based on running test spec files in multiple workers, not the `test` blocks within the same file. Having too many `test` blocks in the same file will be slowly run in the single thread and in case of flakiness, it means Team lose more test coverage than they probably expect. Before (**59** test blocks - **8-8.5 min** per distro): <img width="1709" alt="Screenshot 2025-02-08 at 18 01 40" src="https://github.com/user-attachments/assets/5fd65a1c-85f9-4594-9dae-3f8e99a005ab" /> After (**15** test blocks - **3.5-4 min** per distro): <img width="1578" alt="Screenshot 2025-02-10 at 18 14 42" src="https://github.com/user-attachments/assets/6846898f-7dd2-4f6b-8bc5-d06741b0b120" /> For reviewers: updated tests are possible to run in 2 parallel workers against the same Kibana/ES instance and run time is dropping to **2.5-3 min** 🚀 . It is up to UX-Logs team to decide if you want to keep parallel run (new tests can be added either to parallel or sequential run) <img width="1578" alt="Screenshot 2025-02-11 at 12 14 30" src="https://github.com/user-attachments/assets/e94113f2-d7f1-470e-a6d5-cb5154d99c41" /> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
000e913e7a
commit
bd13e82949
41 changed files with 1086 additions and 1183 deletions
|
@ -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
|
||||
|
||||
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -919,6 +919,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
|
||||
|
|
|
@ -1489,6 +1489,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",
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Page['goto']>[1]) => ReturnType<Page['goto']>;
|
||||
gotoApp: (appName: string, pathOptions?: PathOptions) => ReturnType<Page['goto']>;
|
||||
/**
|
||||
* Waits for the Kibana loading spinner indicator to disappear.
|
||||
* @returns A Promise resolving when the indicator is hidden.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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<void>;
|
||||
delete: (name: string) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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' },
|
||||
],
|
||||
});
|
|
@ -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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
```
|
|
@ -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';
|
|
@ -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: ['<rootDir>/x-pack/solutions/observability/packages/kbn-scout-oblt'],
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type": "test-helper",
|
||||
"id": "@kbn/scout-oblt",
|
||||
"owner": "@elastic/appex-qa",
|
||||
"devOnly": true
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/scout-oblt",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "Elastic License 2.0"
|
||||
}
|
|
@ -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';
|
|
@ -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<ObltParallelTestFixtures, ObltParallelWorkerFixtures>({
|
||||
pageObjects: async (
|
||||
{
|
||||
pageObjects,
|
||||
page,
|
||||
}: {
|
||||
pageObjects: ObltParallelTestFixtures['pageObjects'];
|
||||
page: ObltParallelTestFixtures['page'];
|
||||
},
|
||||
use: (pageObjects: ObltParallelTestFixtures['pageObjects']) => Promise<void>
|
||||
) => {
|
||||
const extendedPageObjects = extendPageObjects(pageObjects, page);
|
||||
await use(extendedPageObjects);
|
||||
},
|
||||
});
|
|
@ -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<ObltTestFixtures, ObltWorkerFixtures>({
|
||||
pageObjects: async (
|
||||
{
|
||||
pageObjects,
|
||||
page,
|
||||
}: {
|
||||
pageObjects: ObltTestFixtures['pageObjects'];
|
||||
page: ObltTestFixtures['page'];
|
||||
},
|
||||
use: (pageObjects: ObltTestFixtures['pageObjects']) => Promise<void>
|
||||
) => {
|
||||
const extendedPageObjects = extendPageObjects(pageObjects, page);
|
||||
await use(extendedPageObjects);
|
||||
},
|
||||
});
|
|
@ -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;
|
|
@ -5,4 +5,5 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { OnboardingHomePage } from './onboarding_home';
|
||||
export * from './fixtures';
|
||||
export type { ObltPageObjects } from './page_objects';
|
|
@ -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}"]`);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/scout",
|
||||
]
|
||||
}
|
|
@ -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`
|
||||
|
|
|
@ -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.',
|
||||
};
|
|
@ -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<ExtendedScoutTestFixtures, ScoutWorkerFixtures>({
|
||||
const testFixtures = mergeTests(base, onboardingApiFixture);
|
||||
|
||||
export const test = testFixtures.extend<ExtendedScoutTestFixtures, ExtendedScoutWorkerFixtures>({
|
||||
pageObjects: async (
|
||||
{
|
||||
pageObjects,
|
||||
page,
|
||||
kbnUrl,
|
||||
kbnClient,
|
||||
}: {
|
||||
pageObjects: ExtendedScoutTestFixtures['pageObjects'];
|
||||
page: ExtendedScoutTestFixtures['page'];
|
||||
kbnUrl: KibanaUrl;
|
||||
kbnClient: KbnClient;
|
||||
},
|
||||
use: (pageObjects: ExtendedScoutTestFixtures['pageObjects']) => Promise<void>
|
||||
) => {
|
||||
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';
|
||||
|
|
|
@ -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<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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' },
|
||||
],
|
||||
});
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
});
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
);
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -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/);
|
||||
});
|
||||
});
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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/);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
|
@ -7,7 +7,8 @@
|
|||
"**/*"
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/scout"
|
||||
"@kbn/scout",
|
||||
"@kbn/scout-oblt"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
|
|
@ -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 ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue