[8.x] Migrate onboarding Cypress tests to Scout (#205482) (#211388)

# Backport

This will backport the following commits from `main` to `8.x`:
- [Migrate onboarding Cypress tests to Scout
(#205482)](https://github.com/elastic/kibana/pull/205482)

<!--- Backport version: 9.6.4 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Mykola
Harmash","email":"mykola.harmash@gmail.com"},"sourceCommit":{"committedDate":"2025-01-10T08:44:29Z","message":"Migrate
onboarding Cypress tests to Scout (#205482)\n\nThis change converts
Cypress tests for the custom logs flow into\r\nPlaywright using [the
Scout\r\nwrapper](https://github.com/elastic/kibana/tree/main/packages/kbn-scout).\r\n\r\n>
[!NOTE]\r\n> As Scout package is still being developed, the PR pipeline
configured\r\nto runs Playwright tests only when code in certain plugins
have been\r\nchanged and not on every PR.\r\n\r\n### How to run tests
locally\r\n\r\nStart the Scout server\r\n```bash\r\nnode
scripts/scout.js start-server --stateful\r\n```\r\n\r\nIn a separate
terminal run the tests\r\n```bash\r\nnpx playwright test --config
x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts\r\n```\r\n\r\nPlaywright
runs browsers in a headless mode by default, user `--headed`\r\noption
if
needed","sha":"6ed214a69f75a69305cbb06515bdb5e9e75ac3ae","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:skip","v9.0.0","ci:project-deploy-observability"],"title":"Migrate
onboarding Cypress tests to
Scout","number":205482,"url":"https://github.com/elastic/kibana/pull/205482","mergeCommit":{"message":"Migrate
onboarding Cypress tests to Scout (#205482)\n\nThis change converts
Cypress tests for the custom logs flow into\r\nPlaywright using [the
Scout\r\nwrapper](https://github.com/elastic/kibana/tree/main/packages/kbn-scout).\r\n\r\n>
[!NOTE]\r\n> As Scout package is still being developed, the PR pipeline
configured\r\nto runs Playwright tests only when code in certain plugins
have been\r\nchanged and not on every PR.\r\n\r\n### How to run tests
locally\r\n\r\nStart the Scout server\r\n```bash\r\nnode
scripts/scout.js start-server --stateful\r\n```\r\n\r\nIn a separate
terminal run the tests\r\n```bash\r\nnpx playwright test --config
x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts\r\n```\r\n\r\nPlaywright
runs browsers in a headless mode by default, user `--headed`\r\noption
if
needed","sha":"6ed214a69f75a69305cbb06515bdb5e9e75ac3ae"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/205482","number":205482,"mergeCommit":{"message":"Migrate
onboarding Cypress tests to Scout (#205482)\n\nThis change converts
Cypress tests for the custom logs flow into\r\nPlaywright using [the
Scout\r\nwrapper](https://github.com/elastic/kibana/tree/main/packages/kbn-scout).\r\n\r\n>
[!NOTE]\r\n> As Scout package is still being developed, the PR pipeline
configured\r\nto runs Playwright tests only when code in certain plugins
have been\r\nchanged and not on every PR.\r\n\r\n### How to run tests
locally\r\n\r\nStart the Scout server\r\n```bash\r\nnode
scripts/scout.js start-server --stateful\r\n```\r\n\r\nIn a separate
terminal run the tests\r\n```bash\r\nnpx playwright test --config
x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts\r\n```\r\n\r\nPlaywright
runs browsers in a headless mode by default, user `--headed`\r\noption
if needed","sha":"6ed214a69f75a69305cbb06515bdb5e9e75ac3ae"}}]}]
BACKPORT-->

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Mykola Harmash 2025-02-17 16:18:59 +01:00 committed by GitHub
parent 7f23710f4a
commit e86ca4b448
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 1270 additions and 2036 deletions

View file

@ -2,7 +2,7 @@ steps:
- command: .buildkite/scripts/steps/functional/scout_ui_tests.sh
label: 'Scout UI Tests'
agents:
machineType: n2-standard-4
machineType: n2-standard-8
preemptible: true
depends_on:
- build
@ -11,7 +11,7 @@ steps:
- linting
- linting_with_types
- check_types
timeout_in_minutes: 30
timeout_in_minutes: 60
retry:
automatic:
- exit_status: '-1'

View file

@ -109,18 +109,6 @@ const getPipeline = (filename: string, removeSteps = true) => {
pipeline.push(getPipeline('.buildkite/pipelines/pull_request/inventory_cypress.yml'));
}
if (
(await doAnyChangesMatch([
/^x-pack\/solutions\/observability\/plugins\/observability_onboarding/,
/^x-pack\/platform\/plugins\/shared\/fleet/,
])) ||
GITHUB_PR_LABELS.includes('ci:all-cypress-suites')
) {
pipeline.push(
getPipeline('.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml')
);
}
if (
(await doAnyChangesMatch([/^x-pack\/solutions\/observability\/plugins\/profiling/])) ||
GITHUB_PR_LABELS.includes('ci:all-cypress-suites')
@ -405,6 +393,7 @@ const getPipeline = (filename: string, removeSteps = true) => {
if (
(await doAnyChangesMatch([
/^x-pack\/platform\/plugins\/private\/discover_enhanced\/ui_tests/,
/^x-pack\/solutions\/observability\/plugins\/observability_onboarding/,
/^packages\/kbn-scout/,
])) ||
GITHUB_PR_LABELS.includes('ci:scout-ui-tests')

View file

@ -31,4 +31,10 @@ for run_mode in "--stateful"; do
run_tests "Maps" "x-pack/platform/plugins/shared/maps/ui_tests/playwright.config.ts" "$run_mode"
done
exit $EXIT_CODE
# Observability Onboarding
for run_mode in "--stateful"; do
run_tests "Observability Onboarding" "x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts" "$run_mode"
done
exit $EXIT_CODE

1
.github/CODEOWNERS vendored
View file

@ -684,7 +684,6 @@ x-pack/solutions/observability/packages/alerting_test_data @elastic/obs-ux-manag
x-pack/test/cases_api_integration/common/plugins/observability @elastic/response-ops
x-pack/solutions/observability/packages/get_padded_alert_time_range_util @elastic/obs-ux-management-team
x-pack/solutions/observability/plugins/observability_logs_explorer @elastic/obs-ux-logs-team
x-pack/solutions/observability/plugins/observability_onboarding/e2e @elastic/obs-ux-logs-team
x-pack/solutions/observability/plugins/observability_onboarding @elastic/obs-ux-logs-team
x-pack/solutions/observability/plugins/observability @elastic/obs-ux-management-team
x-pack/solutions/observability/plugins/observability_shared @elastic/observability-ui

View file

@ -1473,7 +1473,6 @@
"@kbn/manifest": "link:packages/kbn-manifest",
"@kbn/mock-idp-plugin": "link:packages/kbn-mock-idp-plugin",
"@kbn/mock-idp-utils": "link:packages/kbn-mock-idp-utils",
"@kbn/observability-onboarding-e2e": "link:x-pack/solutions/observability/plugins/observability_onboarding/e2e",
"@kbn/observability-synthetics-test-data": "link:x-pack/solutions/observability/packages/synthetics_test_data",
"@kbn/openapi-bundler": "link:packages/kbn-openapi-bundler",
"@kbn/openapi-generator": "link:packages/kbn-openapi-generator",

View file

@ -1362,8 +1362,6 @@
"@kbn/observability-get-padded-alert-time-range-util/*": ["x-pack/solutions/observability/packages/get_padded_alert_time_range_util/*"],
"@kbn/observability-logs-explorer-plugin": ["x-pack/solutions/observability/plugins/observability_logs_explorer"],
"@kbn/observability-logs-explorer-plugin/*": ["x-pack/solutions/observability/plugins/observability_logs_explorer/*"],
"@kbn/observability-onboarding-e2e": ["x-pack/solutions/observability/plugins/observability_onboarding/e2e"],
"@kbn/observability-onboarding-e2e/*": ["x-pack/solutions/observability/plugins/observability_onboarding/e2e/*"],
"@kbn/observability-onboarding-plugin": ["x-pack/solutions/observability/plugins/observability_onboarding"],
"@kbn/observability-onboarding-plugin/*": ["x-pack/solutions/observability/plugins/observability_onboarding/*"],
"@kbn/observability-plugin": ["x-pack/solutions/observability/plugins/observability"],

View file

@ -1,51 +0,0 @@
# Observability onboarding E2E tests
Observability onboarding uses [FTR](../../../../../packages/kbn-test/README.mdx) (functional test runner) and [Cypress](https://www.cypress.io/) to run the e2e tests. The tests are located at `kibana/x-pack/solutions/observability/plugins/observability_onboarding/e2e/cypress/e2e`.
## E2E Tests (Cypress)
The E2E tests are located in [`x-pack/solutions/observability/plugins/observability_onboarding/e2e`](./cypress/e2e).
Tests run on buildkite PR pipeline are parallelized (2 parallel jobs) and are orchestrated by the Cypress dashboard service. It can be configured in [.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml](https://github.com/elastic/kibana/blob/main/.buildkite/pipelines/pull_request/observability_onboarding_cypress.yml) with the property `parallelism`.
```yml
...
depends_on: build
parallelism: 2
...
```
## Running it locally
### Start test server
```
node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e --server
```
### Run tests
Runs all tests in the terminal
```
node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e --runner
```
### Open cypress dashboard
Opens cypress dashboard, there it's possible to select what test you want to run.
```
node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e --open
```
### Arguments
| Option | Description |
| ------------ | ----------------------------------------------- |
| --server | Only start ES and Kibana |
| --runner | Only run tests |
| --spec | Specify the specs to run |
| --times | Repeat the test n number of times |
| --bail | stop tests after the first failure |
```
node x-pack/solutions/observability/plugins/observability_onboarding/scripts/test/e2e.js --runner --spec cypress/e2e/home.cy.ts --times 2
```

View file

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { defineCypressConfig } from '@kbn/cypress-config';
export default defineCypressConfig({
fileServerFolder: './cypress',
fixturesFolder: './cypress/fixtures',
screenshotsFolder: './cypress/screenshots',
videosFolder: './cypress/videos',
requestTimeout: 10000,
responseTimeout: 40000,
defaultCommandTimeout: 30000,
execTimeout: 120000,
pageLoadTimeout: 120000,
viewportHeight: 1800,
viewportWidth: 1440,
video: false,
screenshotOnRunFailure: false,
retries: {
runMode: 1,
},
e2e: {
baseUrl: 'http://localhost:5601',
supportFile: './cypress/support/e2e.ts',
specPattern: './cypress/e2e/**/*.cy.{js,jsx,ts,tsx}',
},
});

View file

@ -1,36 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// Failing: See https://github.com/elastic/kibana/issues/183341
describe.skip('[Observability onboarding] Landing page', () => {
beforeEach(() => {
cy.loginAsElastic();
});
describe('Entry point', () => {
it('when clicking on the logs card the user is navigated to the observability onboarding page', () => {
cy.getByTestSubj('guideButtonRedirect').click();
cy.getByTestSubj('guide-filter-observability').click();
cy.getByTestSubj('onboarding--observability--logs').click();
cy.url().should('include', '/app/observabilityOnboarding');
});
it('when clicking on observability overview callout the user is navigated to the observability onboarding page', () => {
cy.visitKibana('/app/observability');
cy.getByTestSubj('observability-onboarding-callout').should('exist');
cy.getByTestSubj('o11yObservabilityOnboardingGetStartedButton').click();
cy.url().should('include', '/app/observabilityOnboarding');
});
});
it('when user navigates to observability onboarding landing page is showed', () => {
cy.visitKibana('/app/observabilityOnboarding');
cy.contains('Onboard Observability data');
});
});

View file

@ -1,280 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
describe('[Logs onboarding] Custom logs - configure step', () => {
describe('logFilePaths', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
});
describe('when user clicks on back button', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
});
it('navigates to observability logs onboarding page', () => {
cy.getByTestSubj('observabilityOnboardingFlowBackToSelectionButton').click();
cy.url().should('include', '/app/observabilityOnboarding');
});
});
it('Users shouldnt be able to continue if logFilePaths is empty', () => {
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').should('not.have.text');
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled');
});
it('Users should be able to continue if logFilePaths is not empty', () => {
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log');
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('not.be.disabled');
});
it('Users can add multiple logFilePaths', () => {
cy.getByTestSubj('obltOnboardingCustomLogsAddFilePath').click();
cy.getByTestSubj('obltOnboardingLogFilePath-0').should('exist');
cy.getByTestSubj('obltOnboardingLogFilePath-1').should('exist');
});
it('Users can delete logFilePaths', () => {
cy.getByTestSubj('obltOnboardingCustomLogsAddFilePath').click();
cy.get('*[data-test-subj^="obltOnboardingLogFilePath-"]').should('have.length', 2);
cy.getByTestSubj('obltOnboardingLogFilePathDelete-1').click();
cy.get('*[data-test-subj^="obltOnboardingLogFilePath-"]').should('have.length', 1);
});
describe('when users fill logFilePaths', () => {
it('datasetname and integration name are auto generated if it is the first path', () => {
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log');
cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').should('have.value', 'mylogs');
cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').should('have.value', 'mylogs');
});
it('datasetname and integration name are not generated if it is not the first path', () => {
cy.getByTestSubj('obltOnboardingCustomLogsAddFilePath').click();
cy.getByTestSubj('obltOnboardingLogFilePath-1').find('input').type('myLogs.log');
cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').should('be.empty');
cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').should('be.empty');
});
});
});
describe('serviceName', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log');
});
it('should be optional allowing user to continue if it is empty', () => {
cy.getByTestSubj('obltOnboardingCustomLogsServiceName').should('not.have.text');
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.enabled');
});
});
describe('advancedSettings', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log');
});
it('Users should expand the content when clicking it', () => {
cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click();
cy.getByTestSubj('obltOnboardingCustomLogsNamespace').should('be.visible');
cy.getByTestSubj('obltOnboardingCustomLogsCustomConfig').should('be.visible');
});
it('Users should hide the content when clicking it', () => {
cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click();
cy.getByTestSubj('obltOnboardingCustomLogsNamespace').should('not.be.visible');
cy.getByTestSubj('obltOnboardingCustomLogsCustomConfig').should('not.be.visible');
});
describe('Namespace', () => {
beforeEach(() => {
cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click();
});
afterEach(() => {
cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click();
});
it('Users should see a default namespace', () => {
cy.getByTestSubj('obltOnboardingCustomLogsNamespace').should('have.value', 'default');
});
it('Users should not be able to continue if they do not specify a namespace', () => {
cy.getByTestSubj('obltOnboardingCustomLogsNamespace').clear();
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled');
});
});
describe('customConfig', () => {
beforeEach(() => {
cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click();
});
afterEach(() => {
cy.getByTestSubj('obltOnboardingCustomLogsAdvancedSettings').click();
});
it('should be optional allowing user to continue if it is empty', () => {
cy.getByTestSubj('obltOnboardingCustomLogsCustomConfig').should('not.have.text');
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.enabled');
});
});
});
describe('integrationName', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log');
});
it('Users should not be able to continue if they do not specify an integrationName', () => {
cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').clear();
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled');
});
it('value will contain _ instead of special chars', () => {
cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').clear().type('hello$world');
cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').should(
'have.value',
'hello_world'
);
});
it('value will be invalid if it is not lowercase', () => {
cy.getByTestSubj('obltOnboardingCustomLogsIntegrationsName').clear().type('H3llowOrld');
cy.contains('An integration name should be lowercase.');
});
});
describe('datasetName', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('myLogs.log');
});
it('Users should not be able to continue if they do not specify a datasetName', () => {
cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').clear();
cy.getByTestSubj('obltOnboardingCustomLogsContinue').should('be.disabled');
});
it('value will contain _ instead of special chars', () => {
cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').clear().type('hello$world');
cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').should('have.value', 'hello_world');
});
it('value will be invalid if it is not lowercase', () => {
cy.getByTestSubj('obltOnboardingCustomLogsDatasetName').clear().type('H3llowOrld');
cy.contains('A dataset name should be lowercase.');
});
});
describe('custom integration', () => {
const CUSTOM_INTEGRATION_NAME = 'mylogs';
beforeEach(() => {
cy.deleteIntegration(CUSTOM_INTEGRATION_NAME);
});
describe('when user is missing privileges', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0')
.find('input')
.type(`${CUSTOM_INTEGRATION_NAME}.log`);
cy.getByTestSubj('obltOnboardingCustomLogsContinue').click();
});
it('installation fails', () => {
cy.getByTestSubj('obltOnboardingCustomIntegrationErrorCallout').should('exist');
});
});
describe('when user has proper privileges', () => {
beforeEach(() => {
cy.loginAsEditorUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0')
.find('input')
.type(`${CUSTOM_INTEGRATION_NAME}.log`);
cy.getByTestSubj('obltOnboardingCustomLogsContinue').click();
});
afterEach(() => {
cy.deleteIntegration(CUSTOM_INTEGRATION_NAME);
});
it('installation succeed and user is redirected install elastic agent step', () => {
cy.url().should('include', '/app/observabilityOnboarding/customLogs/installElasticAgent');
});
});
it('installation fails if integration already exists', () => {
cy.loginAsEditorUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.installCustomIntegration(CUSTOM_INTEGRATION_NAME);
cy.getByTestSubj('obltOnboardingLogFilePath-0')
.find('input')
.type(`${CUSTOM_INTEGRATION_NAME}.log`);
cy.getByTestSubj('obltOnboardingCustomLogsContinue').click();
cy.contains(
'Failed to create the integration as an installation with the name mylogs already exists.'
);
});
describe('when an error occurred on creation', () => {
before(() => {
cy.intercept('/api/fleet/epm/custom_integrations', {
statusCode: 500,
body: {
message: 'Internal error',
},
});
cy.loginAsEditorUser();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.getByTestSubj('obltOnboardingLogFilePath-0')
.find('input')
.type(`${CUSTOM_INTEGRATION_NAME}.log`);
cy.getByTestSubj('obltOnboardingCustomLogsContinue').click();
});
it('user should see the error displayed', () => {
cy.getByTestSubj('obltOnboardingCustomIntegrationErrorCallout').should('exist');
});
});
});
});

View file

@ -1,497 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
describe('[Logs onboarding] Custom logs - install elastic agent', () => {
const CUSTOM_INTEGRATION_NAME = 'mylogs';
const configureCustomLogs = (loginFn = () => cy.loginAsLogMonitoringUser()) => {
loginFn();
cy.visitKibana('/app/observabilityOnboarding/customLogs');
cy.deleteIntegration(CUSTOM_INTEGRATION_NAME);
cy.getByTestSubj('obltOnboardingLogFilePath-0').find('input').type('mylogs.log');
cy.getByTestSubj('obltOnboardingCustomLogsContinue').click();
};
describe('custom integration', () => {
beforeEach(() => {
configureCustomLogs(() => cy.loginAsEditorUser());
});
it('Users should be able to see the custom integration success callout', () => {
cy.getByTestSubj('obltOnboardingCustomIntegrationInstalled').should('be.visible');
});
});
describe('ApiKey generation', () => {
describe('when user is missing privileges', () => {
beforeEach(() => {
configureCustomLogs(() => cy.loginAsEditorUser());
});
it('apiKey is not generated', () => {
cy.getByTestSubj('obltOnboardingLogsApiKeyCreationNoPrivileges').should('exist');
});
});
describe('when user has proper privileges', () => {
beforeEach(() => {
configureCustomLogs();
});
it('apiKey is generated', () => {
cy.getByTestSubj('obltOnboardingLogsApiKeyCreated').should('exist');
});
});
describe('when an error occurred on creation', () => {
before(() => {
cy.intercept('/internal/observability_onboarding/logs/flow', {
statusCode: 500,
body: {
message: 'Internal error',
},
});
configureCustomLogs();
});
it('apiKey is not generated', () => {
cy.getByTestSubj('obltOnboardingLogsApiKeyCreationFailed').should('exist');
});
});
});
describe('Install the Elastic Agent step', () => {
beforeEach(() => {
cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as(
'createOnboardingFlow'
);
configureCustomLogs();
});
describe('When user select Linux OS', () => {
it('Auto download config to host is disabled by default', () => {
cy.get('.euiButtonGroup').contains('Linux').click();
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig')
.should('be.enabled')
.should('not.be.checked');
});
it('Installation script is shown', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('exist');
});
});
describe('When user select Mac OS', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('MacOS').click();
});
it('Auto download config to host is disabled by default', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig')
.should('be.enabled')
.should('not.be.checked');
});
it('Installation script is shown', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('exist');
});
});
describe('When user select Windows OS', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('Windows').click();
});
it('Auto download config to host is disabled by default', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig')
.should('be.disabled')
.should('not.be.checked');
});
it('A link to the documentation is shown instead of installation script', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentWindowsDocsLink').should('exist');
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('not.exist');
});
});
describe('When Auto download config', () => {
describe('is selected', () => {
it('autoDownloadConfig flag is added to installation script', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfigCallout').should(
'exist'
);
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('contain', 'autoDownloadConfig=1');
});
it('Download config button is disabled', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.getByTestSubj('obltOnboardingConfigureElasticAgentStepDownloadConfig').should(
'be.disabled'
);
});
});
it('is not selected autoDownloadConfig flag is not added to installation script', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('not.contain', 'autoDownloadConfig=1');
});
});
describe('When user executes the installation script in the host', () => {
let onboardingId: string;
describe('updates on steps are shown in the flow', () => {
beforeEach(() => {
cy.wait('@createOnboardingFlow')
.its('response.body')
.then((body) => {
onboardingId = body.onboardingId;
});
});
describe('Download elastic Agent step', () => {
it('shows a loading callout when elastic agent is downloading', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Downloading Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent is downloaded', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent downloaded')
.should('exist');
});
it('shows a danger callout when elastic agent was not downloaded', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'danger');
cy.getByTestSubj('obltOnboardingStepStatus-danger')
.contains('Download Elastic Agent')
.should('exist');
});
});
describe('Extract elastic Agent step', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
});
it('shows a loading callout when elastic agent is extracting', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Extracting Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent is extracted', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent extracted')
.should('exist');
});
it('shows a danger callout when elastic agent was not extracted', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'danger');
cy.getByTestSubj('obltOnboardingStepStatus-danger')
.contains('Extract Elastic Agent')
.should('exist');
});
});
describe('Install elastic Agent step', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
});
it('shows a loading callout when elastic agent is installing', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Installing Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent is installed', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent installed')
.should('exist');
});
it('shows a danger callout when elastic agent was not installed', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'danger');
cy.getByTestSubj('obltOnboardingStepStatus-danger')
.contains('Install Elastic Agent')
.should('exist');
});
});
describe('Check elastic Agent status step', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
});
it('shows a loading callout when getting elastic agent status', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Connecting to the Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent status is healthy', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Connected to the Elastic Agent')
.should('exist');
});
it('shows a warning callout when elastic agent status is not healthy', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'warning');
cy.getByTestSubj('obltOnboardingStepStatus-warning')
.contains('Connect to the Elastic Agent')
.should('exist');
});
});
});
});
});
describe('Configure Elastic Agent step', () => {
let onboardingId: string;
beforeEach(() => {
cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as(
'createOnboardingFlow'
);
configureCustomLogs();
cy.wait('@createOnboardingFlow')
.its('response.body')
.then((body) => {
onboardingId = body.onboardingId;
});
});
describe('When user select Linux OS', () => {
beforeEach(() => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
});
it('shows loading callout when config is being downloaded to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Downloading Elastic Agent config')
.should('exist');
});
it('shows success callout when the configuration has been written to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent config written to /opt/Elastic/Agent/elastic-agent.yml')
.should('exist');
});
it('shows warning callout when the configuration was not written in the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-warning"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-warning')
.contains('Configure the agent')
.should('exist');
});
});
describe('When user select Mac OS', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('MacOS').click();
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
});
it('shows loading callout when config is being downloaded to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Downloading Elastic Agent config')
.should('exist');
});
it('shows success callout when the configuration has been written to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent config written to /Library/Elastic/Agent/elastic-agent.yml')
.should('exist');
});
it('shows warning callout when the configuration was not written in the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-warning"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-warning')
.contains('Configure the agent')
.should('exist');
});
});
describe('When user select Windows', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('Windows').click();
});
it('step is disabled', () => {
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]'
).should('exist');
});
});
});
describe('Check logs step', () => {
let onboardingId: string;
beforeEach(() => {
cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as(
'createOnboardingFlow'
);
configureCustomLogs();
cy.wait('@createOnboardingFlow')
.its('response.body')
.then((body) => {
onboardingId = body.onboardingId;
});
});
describe('When user select Linux OS or MacOS', () => {
describe('When configure Elastic Agent step is not finished', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading');
});
it('check logs is not triggered', () => {
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-incomplete"]'
).should('exist');
cy.get('.euiStep__title').contains('Ship logs to Elastic Observability').should('exist');
});
});
describe('When configure Elastic Agent step has finished', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
});
it('shows loading callout when logs are being checked', () => {
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
).should('exist');
cy.get('.euiStep__title').contains('Waiting for logs to be shipped...').should('exist');
});
});
});
describe('When user select Windows', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('Windows').click();
});
it('step is disabled', () => {
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]'
).should('exist');
});
});
});
describe('When logs are being shipped', () => {
beforeEach(() => {
cy.intercept('GET', '**/progress', {
status: 200,
body: {
progress: {
'ea-download': { status: 'complete' },
'ea-extract': { status: 'complete' },
'ea-install': { status: 'complete' },
'ea-status': { status: 'complete' },
'ea-config': { status: 'complete' },
'logs-ingest': { status: 'complete' },
},
},
}).as('checkOnboardingProgress');
configureCustomLogs();
});
it('shows success callout when logs has arrived to elastic', () => {
cy.wait('@checkOnboardingProgress');
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
).should('exist');
cy.get('.euiStep__title').contains('Logs are being shipped!').should('exist');
});
it('when user clicks on Explore Logs it navigates to observability logs explorer', () => {
cy.wait('@checkOnboardingProgress');
cy.getByTestSubj('obltOnboardingExploreLogs').should('exist').click();
cy.url().should('include', '/app/observability-logs-explorer');
cy.get('[data-test-subj="dataSourceSelectorPopoverButton"]')
.contains('[Mylogs] mylogs', { matchCase: false })
.should('exist');
});
});
});

View file

@ -1,28 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// Failing: See https://github.com/elastic/kibana/issues/183341
describe.skip('[Logs onboarding] Give Feedback', () => {
beforeEach(() => {
cy.loginAsElastic();
cy.visitKibana('/app/observabilityOnboarding');
});
it('feedback button is present in system logs onboarding', () => {
cy.getByTestSubj('obltOnboardingHomeStartSystemLogStream').click();
cy.getByTestSubj('observabilityOnboardingPageGiveFeedback').should('exist');
});
it('feedback button is present in custom logs onboarding', () => {
cy.getByTestSubj('obltOnboardingHomeStartLogFileStream').click();
cy.getByTestSubj('observabilityOnboardingPageGiveFeedback').should('exist');
});
it('feedback button is not present in the landing page', () => {
cy.getByTestSubj('observabilityOnboardingPageGiveFeedback').should('not.exist');
});
});

View file

@ -1,562 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
describe('[Logs onboarding] System logs', () => {
describe('System integration', () => {
beforeEach(() => {
cy.deleteIntegration('system');
});
describe('when user clicks on back button', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('navigates to observability logs onboarding page', () => {
cy.getByTestSubj('observabilityOnboardingFlowBackToSelectionButton').click();
cy.url().should('include', '/app/observabilityOnboarding');
});
});
describe('when user is missing privileges', () => {
beforeEach(() => {
cy.loginAsViewerUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('installation fails', () => {
cy.getByTestSubj('obltOnboardingSystemLogsIntegrationInstallationFailed').should('exist');
});
});
describe('when user has proper privileges', () => {
beforeEach(() => {
cy.loginAsEditorUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
after(() => {
cy.deleteIntegration('system');
});
it('installation succeed', () => {
cy.getByTestSubj('obltOnboardingSystemLogsIntegrationInstalled').should('exist');
});
it('show link to navigate to system integration when clicking info icon', () => {
cy.getByTestSubj('obltOnboardingSystemLogsIntegrationInstalled').should('exist');
cy.getByTestSubj('obltOnboardingSystemLogsIntegrationInfo').should('exist').click();
cy.getByTestSubj('observabilityOnboardingSystemIntegrationLearnMore').should('exist');
});
});
});
describe('ApiKey generation', () => {
describe('when user is missing privileges', () => {
it('apiKey is not generated', () => {
cy.loginAsEditorUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
cy.getByTestSubj('obltOnboardingLogsApiKeyCreationNoPrivileges').should('exist');
});
});
describe('when user has proper privileges', () => {
beforeEach(() => {
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('apiKey is generated', () => {
cy.getByTestSubj('obltOnboardingLogsApiKeyCreated').should('exist');
});
});
describe('when an error occurred on creation', () => {
before(() => {
cy.intercept('/internal/observability_onboarding/logs/flow', {
statusCode: 500,
body: {
message: 'Internal error',
},
});
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('apiKey is not generated', () => {
cy.getByTestSubj('obltOnboardingLogsApiKeyCreationFailed').should('exist');
});
});
});
describe('Install the Elastic Agent step', () => {
beforeEach(() => {
cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as(
'createOnboardingFlow'
);
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
describe('When user select Linux OS', () => {
it('Auto download config to host is disabled by default', () => {
cy.get('.euiButtonGroup').contains('Linux').click();
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig')
.should('be.enabled')
.should('not.be.checked');
});
it('Installation script is shown', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('exist');
});
});
describe('When user select Mac OS', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('MacOS').click();
});
it('Auto download config to host is disabled by default', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig')
.should('be.enabled')
.should('not.be.checked');
});
it('Installation script is shown', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('exist');
});
});
describe('When user select Windows OS', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('Windows').click();
});
it('Auto download config to host is disabled by default', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig')
.should('be.disabled')
.should('not.be.checked');
});
it('A link to the documentation is shown instead of installation script', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentWindowsDocsLink').should('exist');
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('not.exist');
});
});
describe('When Auto download config', () => {
describe('is selected', () => {
it('autoDownloadConfig flag is added to installation script', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfigCallout').should(
'exist'
);
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('contain', 'autoDownloadConfig=1');
});
it('Download config button is disabled', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.getByTestSubj('obltOnboardingConfigureElasticAgentStepDownloadConfig').should(
'be.disabled'
);
});
});
it('is not selected autoDownloadConfig flag is not added to installation script', () => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentStep')
.get('.euiCodeBlock')
.should('not.contain', 'autoDownloadConfig=1');
});
});
describe('When user executes the installation script in the host', () => {
let onboardingId: string;
describe('updates on steps are shown in the flow', () => {
beforeEach(() => {
cy.wait('@createOnboardingFlow')
.its('response.body')
.then((body) => {
onboardingId = body.onboardingId;
});
});
describe('Download elastic Agent step', () => {
it('shows a loading callout when elastic agent is downloading', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Downloading Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent is downloaded', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent downloaded')
.should('exist');
});
it('shows a danger callout when elastic agent was not downloaded', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'danger');
cy.getByTestSubj('obltOnboardingStepStatus-danger')
.contains('Download Elastic Agent')
.should('exist');
});
});
describe('Extract elastic Agent step', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
});
it('shows a loading callout when elastic agent is extracting', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Extracting Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent is extracted', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent extracted')
.should('exist');
});
it('shows a danger callout when elastic agent was not extracted', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'danger');
cy.getByTestSubj('obltOnboardingStepStatus-danger')
.contains('Extract Elastic Agent')
.should('exist');
});
});
describe('Install elastic Agent step', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
});
it('shows a loading callout when elastic agent is installing', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Installing Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent is installed', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent installed')
.should('exist');
});
it('shows a danger callout when elastic agent was not installed', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'danger');
cy.getByTestSubj('obltOnboardingStepStatus-danger')
.contains('Install Elastic Agent')
.should('exist');
});
});
describe('Check elastic Agent status step', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
});
it('shows a loading callout when getting elastic agent status', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Connecting to the Elastic Agent')
.should('exist');
});
it('shows a success callout when elastic agent status is healthy', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Connected to the Elastic Agent')
.should('exist');
});
it('shows a warning callout when elastic agent status is not healthy', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'warning');
cy.getByTestSubj('obltOnboardingStepStatus-warning')
.contains('Connect to the Elastic Agent')
.should('exist');
});
});
});
});
});
describe('Configure Elastic Agent step', () => {
let onboardingId: string;
beforeEach(() => {
cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as(
'createOnboardingFlow'
);
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
cy.wait('@createOnboardingFlow')
.its('response.body')
.then((body) => {
onboardingId = body.onboardingId;
});
});
describe('When user select Linux OS', () => {
beforeEach(() => {
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
});
it('shows loading callout when config is being downloaded to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Downloading Elastic Agent config')
.should('exist');
});
it('shows success callout when the configuration has been written to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent config written to /opt/Elastic/Agent/elastic-agent.yml')
.should('exist');
});
it('shows warning callout when the configuration was not written in the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-warning"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-warning')
.contains('Configure the agent')
.should('exist');
});
});
describe('When user select Mac OS', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('MacOS').click();
cy.getByTestSubj('obltOnboardingInstallElasticAgentAutoDownloadConfig').click();
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
});
it('shows loading callout when config is being downloaded to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-loading')
.contains('Downloading Elastic Agent config')
.should('exist');
});
it('shows success callout when the configuration has been written to the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-complete')
.contains('Elastic Agent config written to /Library/Elastic/Agent/elastic-agent.yml')
.should('exist');
});
it('shows warning callout when the configuration was not written in the host', () => {
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning');
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-warning"]'
).should('exist');
cy.getByTestSubj('obltOnboardingStepStatus-warning')
.contains('Configure the agent')
.should('exist');
});
});
describe('When user select Windows', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('Windows').click();
});
it('step is disabled', () => {
cy.get(
'[data-test-subj="obltOnboardingConfigureElasticAgentStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]'
).should('exist');
});
});
});
describe('Check logs step', () => {
let onboardingId: string;
beforeEach(() => {
cy.intercept('POST', '/internal/observability_onboarding/logs/flow').as(
'createOnboardingFlow'
);
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
cy.wait('@createOnboardingFlow')
.its('response.body')
.then((body) => {
onboardingId = body.onboardingId;
});
});
describe('When user select Linux OS or MacOS', () => {
describe('When configure Elastic Agent step is not finished', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading');
});
it('check logs is not triggered', () => {
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-incomplete"]'
).should('exist');
cy.get('.euiStep__title').contains('Ship logs to Elastic Observability').should('exist');
});
});
describe('When configure Elastic Agent step has finished', () => {
beforeEach(() => {
cy.updateInstallationStepStatus(onboardingId, 'ea-download', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
cy.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
cy.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
});
it('shows loading callout when logs are being checked', () => {
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
).should('exist');
cy.get('.euiStep__title').contains('Waiting for logs to be shipped...').should('exist');
});
});
});
describe('When user select Windows', () => {
beforeEach(() => {
cy.get('.euiButtonGroup').contains('Windows').click();
});
it('step is disabled', () => {
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]'
).should('exist');
});
});
});
describe('When logs are being shipped', () => {
beforeEach(() => {
cy.intercept('GET', '**/progress', {
status: 200,
body: {
progress: {
'ea-download': { status: 'complete' },
'ea-extract': { status: 'complete' },
'ea-install': { status: 'complete' },
'ea-status': { status: 'complete' },
'ea-config': { status: 'complete' },
'logs-ingest': { status: 'complete' },
},
},
}).as('checkOnboardingProgress');
cy.intercept('GET', '/api/fleet/epm/packages/system').as('systemIntegrationInstall');
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('shows success callout when logs has arrived to elastic', () => {
cy.wait('@checkOnboardingProgress');
cy.get(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
).should('exist');
cy.get('.euiStep__title').contains('Logs are being shipped!').should('exist');
});
});
describe('Explore Logs', () => {
describe('when integration installation fails', () => {
beforeEach(() => {
cy.deleteIntegration('system');
cy.intercept('GET', '/api/fleet/epm/packages/system', {
statusCode: 500,
body: {
message: 'Internal error',
},
}).as('systemIntegrationInstall');
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('when users clicks on Explore logs they navigate to logs explorer - All logs', () => {
cy.wait('@systemIntegrationInstall');
cy.getByTestSubj('obltOnboardingExploreLogs').should('exist').click();
cy.url().should('include', '/app/observability-logs-explorer');
cy.get('button').contains('All logs').should('exist');
});
});
describe('when integration installation succeed', () => {
beforeEach(() => {
cy.deleteIntegration('system');
cy.intercept('GET', '/api/fleet/epm/packages/system').as('systemIntegrationInstall');
cy.loginAsLogMonitoringUser();
cy.visitKibana('/app/observabilityOnboarding/systemLogs');
});
it('when users clicks on Explore logs they navigate to logs explorer and System integration is selected', () => {
cy.wait('@systemIntegrationInstall');
cy.getByTestSubj('obltOnboardingExploreLogs').should('exist').click();
cy.url().should('include', '/app/observability-logs-explorer');
cy.get('button').contains('[system] system.syslog').should('exist');
});
});
});
});

View file

@ -1,60 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
// Failing: See https://github.com/elastic/kibana/issues/183341
describe.skip('[Observability onboarding] Navigation', () => {
beforeEach(() => {
cy.loginAsElastic();
cy.visitKibana('/app/observabilityOnboarding/');
});
describe('When user clicks on the card', () => {
it('navigates to system logs onboarding', () => {
cy.getByTestSubj('obltOnboardingHomeStartSystemLogStream').click();
cy.url().should('include', '/app/observabilityOnboarding/systemLogs');
});
it('navigates to custom logs onboarding', () => {
cy.getByTestSubj('obltOnboardingHomeStartLogFileStream').click();
cy.url().should('include', '/app/observabilityOnboarding/customLogs');
});
it('navigates to apm tutorial', () => {
cy.getByTestSubj('obltOnboardingHomeStartApmTutorial').click();
cy.url().should('include', '/app/home#/tutorial/apm');
});
it('navigates to kubernetes integration', () => {
cy.getByTestSubj('obltOnboardingHomeGoToKubernetesIntegration').click();
cy.url().should('include', '/app/integrations/detail/kubernetes/overview');
});
it('navigates to integrations', () => {
cy.getByTestSubj('obltOnboardingHomeExploreIntegrations').click();
cy.url().should('include', '/app/integrations/browse');
});
});
describe('When user clicks on Quick links', () => {
it('navigates to use sample data', () => {
cy.getByTestSubj('obltOnboardingHomeUseSampleData').click();
cy.url().should('include', '/app/home#/tutorial_directory/sampleData');
});
it('navigates to upload a file', () => {
cy.getByTestSubj('obltOnboardingHomeUploadAFile').click();
cy.url().should('include', '/app/home#/tutorial_directory/fileDataViz');
});
});
});

View file

@ -1,182 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import URL from 'url';
import { ObservabilityOnboardingUsername } from '@kbn/observability-onboarding-plugin/server/test_helpers/create_observability_onboarding_users/authentication';
export type InstallationStep =
| 'ea-download'
| 'ea-extract'
| 'ea-install'
| 'ea-status'
| 'ea-config';
export type InstallationStepStatus =
| 'incomplete'
| 'complete'
| 'disabled'
| 'loading'
| 'warning'
| 'danger'
| 'current';
export interface ElasticAgentStepPayload {
agentId: string;
}
Cypress.Commands.add('loginAsViewerUser', () => {
return cy.loginAs({
username: ObservabilityOnboardingUsername.viewerUser,
password: 'changeme',
});
});
Cypress.Commands.add('loginAsEditorUser', () => {
return cy.loginAs({
username: ObservabilityOnboardingUsername.editorUser,
password: 'changeme',
});
});
Cypress.Commands.add('loginAsLogMonitoringUser', () => {
return cy.loginAs({
username: ObservabilityOnboardingUsername.logMonitoringUser,
password: 'changeme',
});
});
Cypress.Commands.add('loginAsElastic', () => {
return cy.loginAs({
username: 'elastic',
password: 'changeme',
});
});
Cypress.Commands.add(
'loginAs',
({ username, password }: { username: string; password: string }) => {
const kibanaUrl = Cypress.env('KIBANA_URL');
cy.log(`Logging in as ${username} on ${kibanaUrl}`);
cy.visit('/');
cy.request({
log: true,
method: 'POST',
url: `${kibanaUrl}/internal/security/login`,
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: `${kibanaUrl}/login`,
params: { username, password },
},
headers: {
'kbn-xsrf': 'e2e_test',
},
});
cy.visit('/');
}
);
Cypress.Commands.add('getByTestSubj', (selector: string) => {
return cy.get(`[data-test-subj="${selector}"]`);
});
Cypress.Commands.add('visitKibana', (url: string, rangeFrom?: string, rangeTo?: string) => {
const urlPath = URL.format({
pathname: url,
query: { rangeFrom, rangeTo },
});
cy.visit(urlPath);
cy.getByTestSubj('kbnLoadingMessage').should('exist');
cy.getByTestSubj('kbnLoadingMessage').should('not.exist', {
timeout: 50000,
});
});
Cypress.Commands.add('installCustomIntegration', (integrationName: string) => {
const kibanaUrl = Cypress.env('KIBANA_URL');
cy.request({
log: false,
method: 'POST',
url: `${kibanaUrl}/api/fleet/epm/custom_integrations`,
body: {
force: true,
integrationName,
datasets: [
{ name: `${integrationName}.access`, type: 'logs' },
{ name: `${integrationName}.error`, type: 'metrics' },
{ name: `${integrationName}.warning`, type: 'logs' },
],
},
headers: {
'kbn-xsrf': 'e2e_test',
'Elastic-Api-Version': '2023-10-31',
},
auth: { user: 'editor', pass: 'changeme' },
});
});
Cypress.Commands.add('deleteIntegration', (integrationName: string) => {
const kibanaUrl = Cypress.env('KIBANA_URL');
cy.request({
log: false,
method: 'GET',
url: `${kibanaUrl}/api/fleet/epm/packages/${integrationName}`,
headers: {
'kbn-xsrf': 'e2e_test',
},
auth: { user: 'editor', pass: 'changeme' },
failOnStatusCode: false,
}).then((response) => {
const status = response.body.item?.status;
if (status === 'installed') {
cy.request({
log: false,
method: 'DELETE',
url: `${kibanaUrl}/api/fleet/epm/packages/${integrationName}`,
body: {
force: false,
},
headers: {
'kbn-xsrf': 'e2e_test',
'Elastic-Api-Version': '2023-10-31',
},
auth: { user: 'editor', pass: 'changeme' },
});
}
});
});
Cypress.Commands.add(
'updateInstallationStepStatus',
(
onboardingId: string,
step: InstallationStep,
status: InstallationStepStatus,
payload: ElasticAgentStepPayload | undefined
) => {
const kibanaUrl = Cypress.env('KIBANA_URL');
cy.log(onboardingId, step, status);
cy.request({
log: false,
method: 'POST',
url: `${kibanaUrl}/internal/observability_onboarding/flow/${onboardingId}/step/${step}`,
headers: {
'kbn-xsrf': 'e2e_test',
},
auth: { user: 'editor', pass: 'changeme' },
body: {
status,
payload,
},
});
}
);

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
declare namespace Cypress {
interface Chainable {
loginAs(params: {
username: string;
password: string;
}): Cypress.Chainable<Cypress.Response<any>>;
loginAsViewerUser(): Cypress.Chainable<Cypress.Response<any>>;
loginAsEditorUser(): Cypress.Chainable<Cypress.Response<any>>;
loginAsLogMonitoringUser(): Cypress.Chainable<Cypress.Response<any>>;
loginAsElastic(): Cypress.Chainable<Cypress.Response<any>>;
getByTestSubj(selector: string): Chainable<JQuery<Element>>;
visitKibana(url: string, rangeFrom?: string, rangeTo?: string): void;
installCustomIntegration(integrationName: string): void;
deleteIntegration(integrationName: string): void;
updateInstallationStepStatus(
onboardingId: string,
step: InstallationStep,
status: InstallationStepStatus,
payload?: ElasticAgentStepPayload
): void;
}
}

View file

@ -1,92 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import cypress from 'cypress';
import path from 'path';
import Url from 'url';
import { createObservabilityOnboardingUsers } from '@kbn/observability-onboarding-plugin/server/test_helpers/create_observability_onboarding_users';
import { FtrProviderContext } from './ftr_provider_context';
export async function cypressTestRunner({
ftrProviderContext: { getService },
cypressExecution,
}: {
ftrProviderContext: FtrProviderContext;
cypressExecution: typeof cypress.run | typeof cypress.open;
}) {
const config = getService('config');
const username = config.get('servers.elasticsearch.username');
const password = config.get('servers.elasticsearch.password');
const kibanaUrl = Url.format({
protocol: config.get('servers.kibana.protocol'),
hostname: config.get('servers.kibana.hostname'),
port: config.get('servers.kibana.port'),
auth: `${username}:${password}`,
});
const esNode = Url.format({
protocol: config.get('servers.elasticsearch.protocol'),
port: config.get('servers.elasticsearch.port'),
hostname: config.get('servers.elasticsearch.hostname'),
auth: `${username}:${password}`,
});
// Creates ObservabilityOnboarding users
await createObservabilityOnboardingUsers({
elasticsearch: { node: esNode, username, password },
kibana: { hostname: kibanaUrl },
});
const esRequestTimeout = config.get('timeouts.esRequestTimeout');
const kibanaUrlWithoutAuth = Url.format({
protocol: config.get('servers.kibana.protocol'),
hostname: config.get('servers.kibana.hostname'),
port: config.get('servers.kibana.port'),
});
const cypressProjectPath = path.join(__dirname);
const { open, ...cypressCliArgs } = getCypressCliArgs();
const res = await cypressExecution({
...cypressCliArgs,
project: cypressProjectPath,
config: {
e2e: {
baseUrl: kibanaUrlWithoutAuth,
},
},
env: {
KIBANA_URL: kibanaUrlWithoutAuth,
ES_NODE: esNode,
ES_REQUEST_TIMEOUT: esRequestTimeout,
TEST_CLOUD: process.env.TEST_CLOUD,
},
});
return res;
}
function getCypressCliArgs(): Record<string, unknown> {
if (!process.env.CYPRESS_CLI_ARGS) {
return {};
}
const { $0, _, ...cypressCliArgs } = JSON.parse(process.env.CYPRESS_CLI_ARGS) as Record<
string,
unknown
>;
const spec =
typeof cypressCliArgs.spec === 'string' && !cypressCliArgs.spec.includes('**')
? `**/${cypressCliArgs.spec}*`
: cypressCliArgs.spec;
return { ...cypressCliArgs, spec };
}

View file

@ -1,58 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext } from '@kbn/test';
import { CA_CERT_PATH } from '@kbn/dev-utils';
import { commonFunctionalServices } from '@kbn/ftr-common-functional-services';
import { commonFunctionalUIServices } from '@kbn/ftr-common-functional-ui-services';
import path from 'path';
const kibanaYamlFilePath = path.join(__dirname, './ftr_kibana.yml');
async function ftrConfig({ readConfigFile }: FtrConfigProviderContext) {
const kibanaCommonTestsConfig = await readConfigFile(
require.resolve('@kbn/test-suites-src/common/config')
);
const xpackFunctionalTestsConfig = await readConfigFile(
require.resolve('@kbn/test-suites-xpack/functional/config.base')
);
return {
...kibanaCommonTestsConfig.getAll(),
services: {
...commonFunctionalServices,
...commonFunctionalUIServices,
},
esTestCluster: {
...xpackFunctionalTestsConfig.get('esTestCluster'),
serverArgs: [
...xpackFunctionalTestsConfig.get('esTestCluster.serverArgs'),
// define custom es server here
// API Keys is enabled at the top level
'xpack.security.enabled=true',
],
},
kbnTestServer: {
...xpackFunctionalTestsConfig.get('kbnTestServer'),
serverArgs: [
...xpackFunctionalTestsConfig.get('kbnTestServer.serverArgs'),
'--home.disableWelcomeScreen=true',
'--csp.strict=false',
'--csp.warnLegacyBrowsers=false',
// define custom kibana server args here
`--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`,
`--config=${kibanaYamlFilePath}`,
],
},
};
}
// eslint-disable-next-line import/no-default-export
export default ftrConfig;

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext } from '@kbn/test';
import cypress from 'cypress';
import { FtrProviderContext } from './ftr_provider_context';
import { cypressTestRunner } from './cypress_test_runner';
async function ftrConfigOpen({ readConfigFile }: FtrConfigProviderContext) {
const kibanaConfig = await readConfigFile(require.resolve('./ftr_config.ts'));
return {
...kibanaConfig.getAll(),
testRunner,
};
}
export async function testRunner(ftrProviderContext: FtrProviderContext) {
await cypressTestRunner({
ftrProviderContext,
cypressExecution: cypress.open,
});
}
// eslint-disable-next-line import/no-default-export
export default ftrConfigOpen;

View file

@ -1,38 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext } from '@kbn/test';
import cypress from 'cypress';
import { cypressTestRunner } from './cypress_test_runner';
import { FtrProviderContext } from './ftr_provider_context';
async function ftrConfigRun({ readConfigFile }: FtrConfigProviderContext) {
const kibanaConfig = await readConfigFile(require.resolve('./ftr_config.ts'));
return {
...kibanaConfig.getAll(),
testRunner,
};
}
async function testRunner(ftrProviderContext: FtrProviderContext) {
const result = await cypressTestRunner({
ftrProviderContext,
cypressExecution: cypress.run,
});
if (
result &&
((result as CypressCommandLine.CypressFailedRunResult)?.status === 'failed' ||
(result as CypressCommandLine.CypressRunResult)?.totalFailed)
) {
process.exit(1);
}
}
// eslint-disable-next-line import/no-default-export
export default ftrConfigRun;

View file

@ -1 +0,0 @@
xpack.cloud.id: 'myDeployment:03ce773830e104e139243c8f053c974d'

View file

@ -1,10 +0,0 @@
{
"type": "test-helper",
"id": "@kbn/observability-onboarding-e2e",
"owner": [
"@elastic/obs-ux-logs-team"
],
"group": "observability",
"visibility": "private",
"devOnly": true
}

View file

@ -1,19 +0,0 @@
{
"extends": "../../../../../../tsconfig.base.json",
"include": ["**/*"],
"exclude": ["tmp", "target/**/*"],
"compilerOptions": {
"outDir": "target/types",
"types": ["cypress", "node", "cypress-real-events"],
"isolatedModules": false
},
"kbn_references": [
{ "path": "../../../../test/tsconfig.json" },
"@kbn/test",
"@kbn/dev-utils",
"@kbn/cypress-config",
"@kbn/observability-onboarding-plugin",
"@kbn/ftr-common-functional-services",
"@kbn/ftr-common-functional-ui-services"
]
}

View file

@ -9,7 +9,8 @@
"public/**/*",
"typings/**/*",
"public/**/*.json",
"server/**/*"
"server/**/*",
"e2e/**/*"
],
"kbn_references": [
"@kbn/core",
@ -44,7 +45,7 @@
"@kbn/custom-integrations-plugin",
"@kbn/server-route-repository-utils",
"@kbn/core-application-browser",
"@kbn/core-plugins-server"
"@kbn/core-plugins-server",
],
"exclude": [
"target/**/*"

View file

@ -0,0 +1,23 @@
## How to run tests
First start the servers:
```bash
// ESS
node scripts/scout.js start-server --stateful
// Serverless
node scripts/scout.js start-server --serverless=[es|oblt|security]
```
Then you can run the tests in another terminal:
```bash
// ESS
npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts --grep @ess
// Serverless
npx playwright test --config x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts --grep @svlOblt
```
Test results are available in `x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/output`

View file

@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import {
test as base,
PageObjects,
createLazyPageObject,
ScoutTestFixtures,
ScoutWorkerFixtures,
KibanaUrl,
KbnClient,
} from '@kbn/scout';
import { OnboardingHomePage } from './page_objects';
import { CustomLogsPage } from './page_objects/custom_logs';
export interface ExtendedScoutTestFixtures extends ScoutTestFixtures {
pageObjects: PageObjects & {
onboardingHomePage: OnboardingHomePage;
customLogsPage: CustomLogsPage;
};
}
export const test = base.extend<ExtendedScoutTestFixtures, ScoutWorkerFixtures>({
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);
},
});

View file

@ -0,0 +1,195 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ScoutPage, KibanaUrl, KbnClient } from '@kbn/scout';
export class CustomLogsPage {
static readonly ASSERTION_MESSAGES = {
INTEGRATION_NAME_CASE_ERROR: 'An integration name should be lowercase.',
DATASET_NAME_CASE_ERROR: 'A dataset name should be lowercase.',
EXISTING_INTEGRATION_ERROR: (name: string) =>
`Failed to create the integration as an installation with the name ${name} already exists.`,
DOWNLOADING_AGENT_STATUS: 'Downloading Elastic Agent',
DOWNLOADED_AGENT_STATUS: 'Elastic Agent downloaded',
DOWNLOAD_AGENT_DANGER_CALLOUT: 'Download Elastic Agent',
EXTRACTING_AGENT_STATUS: 'Extracting Elastic Agent',
EXTRACTED_AGENT_STATUS: 'Elastic Agent extracted',
EXTRACT_AGENT_DANGER_CALLOUT: 'Extract Elastic Agent',
INSTALLING_AGENT_STATUS: 'Installing Elastic Agent',
INSTALLED_AGENT_STATUS: 'Elastic Agent installed',
INSTALL_AGENT_DANGER_CALLOUT: 'Install Elastic Agent',
CONNECTING_TO_AGENT_STATUS: 'Connecting to the Elastic Agent',
CONNECTED_TO_AGENT_STATUS: 'Connected to the Elastic Agent',
CONNECT_AGENT_WARNING_CALLOUT: 'Connect to the Elastic Agent',
DOWNLOAD_AGENT_CONFIG_STATUS: 'Downloading Elastic Agent config',
CONFIGURE_AGENT_WARNING_CALLOUT: 'Configure the agent',
DOWNLOADING_AGENT_CONFIG_STATUS: 'Downloading Elastic Agent config',
AGENT_CONFIGURATION_SUCCESS_CALLOUT_MACOS:
'Elastic Agent config written to /Library/Elastic/Agent/elastic-agent.yml',
AGENT_CONFIGURATION_SUCCESS_CALLOUT_LINUX:
'Elastic Agent config written to /opt/Elastic/Agent/elastic-agent.yml',
INSTALLATION_STEP_2_DISABLED: 'Step 2 is disabled',
};
public readonly advancedSettingsContent;
public readonly logFilePathList;
public readonly continueButton;
public readonly addLogFilePathButton;
public readonly integrationNameInput;
public readonly datasetNameInput;
public readonly serviceNameInput;
public readonly namespaceInput;
public readonly customConfigInput;
public readonly customIntegrationSuccessCallout;
public readonly customIntegrationErrorCallout;
public readonly apiKeyCreateSuccessCallout;
public readonly apiKeyPrivilegesErrorCallout;
public readonly apiKeyCreateErrorCallout;
public readonly linuxCodeSnippetButton;
public readonly macOSCodeSnippetButton;
public readonly windowsCodeSnippetButton;
public readonly autoDownloadConfigurationToggle;
public readonly autoDownloadConfigurationCallout;
public readonly installCodeSnippet;
public readonly windowsInstallElasticAgentDocLink;
public readonly configureElasticAgentStep;
public readonly downloadConfigurationButton;
public readonly stepStatusLoading;
public readonly stepStatusComplete;
public readonly stepStatusDanger;
public readonly stepStatusWarning;
constructor(
private readonly page: ScoutPage,
private readonly kbnUrl: KibanaUrl,
private readonly kbnClient: KbnClient
) {
this.advancedSettingsContent = this.page.testSubj
.locator('obltOnboardingCustomLogsAdvancedSettings')
.getByRole('group');
this.logFilePathList = this.page.locator(`[data-test-subj^=obltOnboardingLogFilePath-]`);
this.continueButton = this.page.testSubj.locator('obltOnboardingCustomLogsContinue');
this.addLogFilePathButton = this.page.testSubj.locator('obltOnboardingCustomLogsAddFilePath');
this.integrationNameInput = this.page.testSubj.locator(
'obltOnboardingCustomLogsIntegrationsName'
);
this.datasetNameInput = this.page.testSubj.locator('obltOnboardingCustomLogsDatasetName');
this.serviceNameInput = this.page.testSubj.locator('obltOnboardingCustomLogsServiceName');
this.namespaceInput = this.page.testSubj.locator('obltOnboardingCustomLogsNamespace');
this.customConfigInput = this.page.testSubj.locator('obltOnboardingCustomLogsCustomConfig');
this.customIntegrationSuccessCallout = this.page.testSubj.locator(
'obltOnboardingCustomIntegrationInstalled'
);
this.customIntegrationErrorCallout = this.page.testSubj.locator(
'obltOnboardingCustomIntegrationErrorCallout'
);
this.apiKeyCreateSuccessCallout = this.page.testSubj.locator('obltOnboardingLogsApiKeyCreated');
this.apiKeyPrivilegesErrorCallout = this.page.testSubj.locator(
'obltOnboardingLogsApiKeyCreationNoPrivileges'
);
this.apiKeyCreateErrorCallout = this.page.testSubj.locator(
'obltOnboardingLogsApiKeyCreationFailed'
);
this.linuxCodeSnippetButton = this.page.testSubj.locator('linux-tar');
this.macOSCodeSnippetButton = this.page.testSubj.locator('macos');
this.windowsCodeSnippetButton = this.page.testSubj.locator('windows');
this.autoDownloadConfigurationToggle = this.page.testSubj.locator(
'obltOnboardingInstallElasticAgentAutoDownloadConfig'
);
this.autoDownloadConfigurationCallout = this.page.testSubj.locator(
'obltOnboardingInstallElasticAgentAutoDownloadConfigCallout'
);
this.installCodeSnippet = this.page.testSubj
.locator('obltOnboardingInstallElasticAgentStep')
.getByRole('code');
this.windowsInstallElasticAgentDocLink = this.page.testSubj.locator(
'obltOnboardingInstallElasticAgentWindowsDocsLink'
);
this.configureElasticAgentStep = this.page.testSubj.locator(
'obltOnboardingConfigureElasticAgentStep'
);
this.downloadConfigurationButton = this.page.testSubj.locator(
'obltOnboardingConfigureElasticAgentStepDownloadConfig'
);
this.stepStatusLoading = this.page.testSubj.locator('obltOnboardingStepStatus-loading');
this.stepStatusComplete = this.page.testSubj.locator('obltOnboardingStepStatus-complete');
this.stepStatusDanger = this.page.testSubj.locator('obltOnboardingStepStatus-danger');
this.stepStatusWarning = this.page.testSubj.locator('obltOnboardingStepStatus-warning');
}
async goto() {
this.page.goto(`${this.kbnUrl.app('observabilityOnboarding')}/customLogs`);
}
async clickBackButton() {
await this.page.testSubj.click('observabilityOnboardingFlowBackToSelectionButton');
}
logFilePathInput(index: number) {
return this.page.testSubj.locator(`obltOnboardingLogFilePath-${index}`).getByRole('textbox');
}
logFilePathDeleteButton(index: number) {
return this.page.testSubj.locator(`obltOnboardingLogFilePathDelete-${index}`);
}
async clickAdvancedSettingsButton() {
return this.page.testSubj
.locator('obltOnboardingCustomLogsAdvancedSettings')
.getByRole('button')
.first()
.click();
}
async installCustomIntegration(name: string) {
await this.kbnClient.request({
method: 'POST',
path: `/api/fleet/epm/custom_integrations`,
body: {
force: true,
integrationName: name,
datasets: [
{ name: `${name}.access`, type: 'logs' },
{ name: `${name}.error`, type: 'metrics' },
{ name: `${name}.warning`, type: 'logs' },
],
},
});
}
async deleteIntegration(name: string) {
const packageInfo = await this.kbnClient.request<{ item: { status: string } }>({
method: 'GET',
path: `/api/fleet/epm/packages/${name}`,
ignoreErrors: [404],
});
if (packageInfo.data.item?.status === 'installed') {
await this.kbnClient.request({
method: 'DELETE',
path: `/api/fleet/epm/packages/${name}`,
});
}
}
async updateInstallationStepStatus(
onboardingId: string,
step: string,
status: string,
payload?: object
) {
await this.kbnClient.request({
method: 'POST',
path: `/internal/observability_onboarding/flow/${onboardingId}/step/${step}`,
body: {
status,
payload,
},
});
}
}

View file

@ -5,8 +5,4 @@
* 2.0.
*/
Cypress.on('uncaught:exception', (err, runnable) => {
return false;
});
import './commands';
export { OnboardingHomePage } from './onboarding_home';

View file

@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ScoutPage } from '@kbn/scout';
export class OnboardingHomePage {
constructor(private readonly page: ScoutPage) {}
async goto() {
this.page.gotoApp('observabilityOnboarding');
}
}

View file

@ -5,6 +5,9 @@
* 2.0.
*/
import { GenericFtrProviderContext } from '@kbn/test';
import { createPlaywrightConfig } from '@kbn/scout';
export type FtrProviderContext = GenericFtrProviderContext<{}, {}>;
// eslint-disable-next-line import/no-default-export
export default createPlaywrightConfig({
testDir: './tests',
});

View file

@ -0,0 +1,256 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { expect } from '@kbn/scout';
import { test } from '../../fixtures';
import { CustomLogsPage } from '../../fixtures/page_objects/custom_logs';
test.describe('Onboarding app - Custom logs configuration', { tag: ['@ess', '@svlOblt'] }, () => {
test.beforeEach(async ({ browserAuth, pageObjects: { customLogsPage } }) => {
await browserAuth.loginAsAdmin();
await customLogsPage.goto();
});
test('When user clicks the back button user goes to the onboarding home page', async ({
pageObjects: { customLogsPage },
page,
}) => {
await customLogsPage.clickBackButton();
expect(page.url()).toContain('/app/observabilityOnboarding');
});
test(`Users shouldn't be able to continue if logFilePaths is empty`, async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.logFilePathInput(0)).toHaveValue('');
await expect(customLogsPage.continueButton).toBeDisabled();
});
test(`Users should be able to continue if logFilePaths is not empty`, async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.logFilePathInput(0).fill('some/path');
await expect(customLogsPage.continueButton).not.toBeDisabled();
});
test('Users can add multiple logFilePaths', async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.addLogFilePathButton.click();
await expect(customLogsPage.logFilePathInput(0)).toBeVisible();
await expect(customLogsPage.logFilePathInput(1)).toBeVisible();
});
test('Users can delete logFilePaths', async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.addLogFilePathButton.click();
await expect(customLogsPage.logFilePathList).toHaveCount(2);
await customLogsPage.logFilePathDeleteButton(1).click();
await expect(customLogsPage.logFilePathList).toHaveCount(1);
});
test('Dataset Name and Integration Name are auto generated if it is the first path', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.logFilePathInput(0).fill('myLogs.log');
await expect(customLogsPage.integrationNameInput).toHaveValue('mylogs');
await expect(customLogsPage.datasetNameInput).toHaveValue('mylogs');
});
test('Dataset Name and Integration Name are not generated if it is not the first path', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.addLogFilePathButton.click();
await customLogsPage.logFilePathInput(1).fill('myLogs.log');
await expect(customLogsPage.integrationNameInput).toHaveValue('');
await expect(customLogsPage.datasetNameInput).toHaveValue('');
});
test('Service name input should be optional allowing user to continue if it is empty', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.logFilePathInput(0).fill('myLogs.log');
await expect(customLogsPage.serviceNameInput).toHaveValue('');
await expect(customLogsPage.continueButton).not.toBeDisabled();
});
test('Users should expand and collapse the Advanced settings section', async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.advancedSettingsContent).not.toBeVisible();
await customLogsPage.clickAdvancedSettingsButton();
await expect(customLogsPage.advancedSettingsContent).toBeVisible();
await customLogsPage.clickAdvancedSettingsButton();
await expect(customLogsPage.advancedSettingsContent).not.toBeVisible();
});
test('Users should see a default namespace', async ({ pageObjects: { customLogsPage } }) => {
customLogsPage.clickAdvancedSettingsButton();
await expect(customLogsPage.namespaceInput).toHaveValue('default');
});
test('Users should not be able to continue if they do not specify a namespace', async ({
pageObjects: { customLogsPage },
}) => {
customLogsPage.clickAdvancedSettingsButton();
await customLogsPage.namespaceInput.fill('');
await expect(customLogsPage.continueButton).toBeDisabled();
});
test('should be optional allowing user to continue if it is empty', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.clickAdvancedSettingsButton();
await customLogsPage.logFilePathInput(0).fill('myLogs.log');
await expect(customLogsPage.customConfigInput).toHaveValue('');
await expect(customLogsPage.continueButton).not.toBeDisabled();
});
test.describe('Integration name', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.logFilePathInput(0).fill('myLogs.log');
});
test('Users should not be able to continue if they do not specify an integrationName', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.integrationNameInput.fill('');
await expect(customLogsPage.continueButton).toBeDisabled();
});
test('value will contain _ instead of special chars', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.integrationNameInput.fill('hello$world');
await expect(customLogsPage.integrationNameInput).toHaveValue('hello_world');
});
test('value will be invalid if it is not lowercase', async ({
pageObjects: { customLogsPage },
page,
}) => {
await customLogsPage.integrationNameInput.fill('H3llowOrld');
await expect(
page.getByText(CustomLogsPage.ASSERTION_MESSAGES.INTEGRATION_NAME_CASE_ERROR)
).toBeVisible();
});
});
test.describe('datasetName', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.logFilePathInput(0).fill('myLogs.log');
});
test('Users should not be able to continue if they do not specify a datasetName', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.datasetNameInput.fill('');
await expect(customLogsPage.continueButton).toBeDisabled();
});
test('value will contain _ instead of special chars', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.datasetNameInput.fill('hello$world');
await expect(customLogsPage.datasetNameInput).toHaveValue('hello_world');
});
test('value will be invalid if it is not lowercase', async ({
pageObjects: { customLogsPage },
page,
}) => {
await customLogsPage.datasetNameInput.fill('H3llowOrld');
await expect(
page.getByText(CustomLogsPage.ASSERTION_MESSAGES.DATASET_NAME_CASE_ERROR)
).toBeVisible();
});
});
test.describe('Custom integration', () => {
const CUSTOM_INTEGRATION_NAME = 'mylogs';
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.deleteIntegration(CUSTOM_INTEGRATION_NAME);
});
test.describe('when user is missing privileges', () => {
test.beforeEach(async ({ browserAuth, page }) => {
await browserAuth.loginAsViewer();
await page.reload();
});
test.afterEach(async ({ browserAuth, page }) => {
await browserAuth.loginAsAdmin();
await page.reload();
});
test('installation fails', async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`);
await customLogsPage.continueButton.click();
await expect(customLogsPage.customIntegrationErrorCallout).toBeVisible();
});
});
test.describe('when user has proper privileges', () => {
test('installation succeed and user is redirected to install elastic agent step', async ({
page,
pageObjects: { customLogsPage },
}) => {
await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`);
await customLogsPage.continueButton.click();
await page.waitForURL('**/app/observabilityOnboarding/customLogs/installElasticAgent');
});
});
test('installation fails if integration already exists', async ({
pageObjects: { customLogsPage },
page,
}) => {
await customLogsPage.installCustomIntegration(CUSTOM_INTEGRATION_NAME);
await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`);
await customLogsPage.continueButton.click();
await expect(
page.getByText(
CustomLogsPage.ASSERTION_MESSAGES.EXISTING_INTEGRATION_ERROR(CUSTOM_INTEGRATION_NAME)
)
).toBeVisible();
});
test.describe('when an error occurred on creation', () => {
test('user should see the error displayed', async ({
pageObjects: { customLogsPage },
page,
kbnUrl,
}) => {
await page.route(kbnUrl.get('/api/fleet/epm/custom_integrations'), (route) => {
route.fulfill({
status: 500,
json: {
message: 'Internal error',
},
});
});
await customLogsPage.logFilePathInput(0).fill(`${CUSTOM_INTEGRATION_NAME}.log`);
await customLogsPage.continueButton.click();
await expect(customLogsPage.customIntegrationErrorCallout).toBeVisible();
});
});
});
});

View file

@ -0,0 +1,696 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { expect } from '@kbn/scout';
import { type ExtendedScoutTestFixtures, test } from '../../fixtures';
import { CustomLogsPage } from '../../fixtures/page_objects/custom_logs';
const CUSTOM_INTEGRATION_NAME = 'mylogs';
function setupPage({ isAdmin }: { isAdmin: boolean }) {
return async ({
browserAuth,
pageObjects: { customLogsPage },
page,
}: ExtendedScoutTestFixtures) => {
await browserAuth.loginAsAdmin();
await customLogsPage.deleteIntegration(CUSTOM_INTEGRATION_NAME);
if (!isAdmin) {
await browserAuth.loginAsPrivilegedUser();
await page.reload();
}
await customLogsPage.deleteIntegration(CUSTOM_INTEGRATION_NAME);
await customLogsPage.goto();
await customLogsPage.logFilePathInput(0).fill('mylogs.log');
await customLogsPage.continueButton.click();
};
}
test.describe(
'Onboarding app - Custom logs install Elastic Agent',
{ tag: ['@ess', '@svlOblt'] },
() => {
test.describe('Custom integration', () => {
test.beforeEach(setupPage({ isAdmin: true }));
test('Users should be able to see the custom integration success callout', async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.customIntegrationSuccessCallout).toBeVisible();
});
});
test.describe('ApiKey generation', () => {
test.describe('when user is missing privileges', () => {
test.beforeEach(setupPage({ isAdmin: false }));
test('apiKey is not generated', async ({ pageObjects: { customLogsPage } }) => {
await expect(customLogsPage.apiKeyPrivilegesErrorCallout).toBeVisible();
});
});
test.describe('when user has proper privileges', () => {
test.beforeEach(setupPage({ isAdmin: true }));
test('apiKey is generated', async ({ pageObjects: { customLogsPage } }) => {
await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible();
});
});
test.describe('when an error occurred on creation', () => {
test.beforeEach(async ({ browserAuth, pageObjects: { customLogsPage }, page }) => {
await page.route('**/observability_onboarding/logs/flow', (route) => {
route.fulfill({
status: 500,
body: JSON.stringify({
message: 'Internal error',
}),
});
});
});
test.beforeEach(setupPage({ isAdmin: true }));
test('apiKey is not generated', async ({ pageObjects: { customLogsPage } }) => {
await expect(customLogsPage.apiKeyCreateErrorCallout).toBeVisible();
});
});
});
test.describe('Install the Elastic Agent step', () => {
test.beforeEach(setupPage({ isAdmin: true }));
test.describe('When user select Linux OS', () => {
test('Auto download config to host is disabled by default', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.linuxCodeSnippetButton.click();
await expect(customLogsPage.autoDownloadConfigurationToggle).not.toBeChecked();
});
test('Installation script is shown', async ({ pageObjects: { customLogsPage } }) => {
await expect(customLogsPage.installCodeSnippet).toBeVisible();
});
});
test.describe('When user select Mac OS', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.macOSCodeSnippetButton.click();
});
test('Auto download config to host is disabled by default', async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.autoDownloadConfigurationToggle).not.toBeChecked();
});
test('Installation script is shown', async ({ pageObjects: { customLogsPage } }) => {
await expect(customLogsPage.installCodeSnippet).toBeVisible();
});
});
test.describe('When user select Windows OS', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.windowsCodeSnippetButton.click();
});
test('Auto download config to host is disabled by default', async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.autoDownloadConfigurationToggle).not.toBeChecked();
});
test('A link to the documentation is shown instead of installation script', async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.windowsInstallElasticAgentDocLink).toBeVisible();
await expect(customLogsPage.installCodeSnippet).not.toBeVisible();
});
});
test.describe('When Auto download config', () => {
test.describe('is selected', () => {
test('autoDownloadConfig flag is added to installation script', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.autoDownloadConfigurationToggle.click();
await expect(customLogsPage.autoDownloadConfigurationCallout).toBeVisible();
await expect(customLogsPage.installCodeSnippet).toContainText('autoDownloadConfig=1');
});
test('Download config button is disabled', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.autoDownloadConfigurationToggle.click();
await expect(customLogsPage.downloadConfigurationButton).toBeDisabled();
});
});
test('is not selected autoDownloadConfig flag is not added to installation script', async ({
pageObjects: { customLogsPage },
}) => {
await expect(customLogsPage.installCodeSnippet).not.toContainText('autoDownloadConfig=1');
});
});
test.describe('When user executes the installation script in the host', () => {
let onboardingId: string;
test.describe('updates on steps are shown in the flow', () => {
test.beforeEach(async ({ page, pageObjects: { customLogsPage } }) => {
await page.route('**/observability_onboarding/logs/flow', async (route) => {
const response = await route.fetch();
const body = await response.json();
onboardingId = body.onboardingId;
await route.fulfill({ response });
});
await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible();
});
test.describe('Download elastic Agent step', () => {
test('shows a loading callout when elastic agent is downloading', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'loading'
);
await expect(
customLogsPage.stepStatusLoading.getByText(
CustomLogsPage.ASSERTION_MESSAGES.DOWNLOADING_AGENT_STATUS
)
).toBeVisible();
});
test('shows a success callout when elastic agent is downloaded', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await expect(
customLogsPage.stepStatusComplete.getByText(
CustomLogsPage.ASSERTION_MESSAGES.DOWNLOADED_AGENT_STATUS
)
).toBeVisible();
});
test('shows a danger callout when elastic agent was not downloaded', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'danger'
);
await expect(
customLogsPage.stepStatusDanger.getByText(
CustomLogsPage.ASSERTION_MESSAGES.DOWNLOAD_AGENT_DANGER_CALLOUT
)
).toBeVisible();
});
});
test.describe('Extract elastic Agent step', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
});
test('shows a loading callout when elastic agent is extracting', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'loading'
);
await expect(
customLogsPage.stepStatusLoading.getByText(
CustomLogsPage.ASSERTION_MESSAGES.EXTRACTING_AGENT_STATUS
)
).toBeVisible();
});
test('shows a success callout when elastic agent is extracted', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'complete'
);
await expect(
customLogsPage.stepStatusComplete.getByText(
CustomLogsPage.ASSERTION_MESSAGES.EXTRACTED_AGENT_STATUS
)
).toBeVisible();
});
test('shows a danger callout when elastic agent was not extracted', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'danger'
);
await expect(
customLogsPage.stepStatusDanger.getByText(
CustomLogsPage.ASSERTION_MESSAGES.EXTRACT_AGENT_DANGER_CALLOUT
)
).toBeVisible();
});
});
test.describe('Install elastic Agent step', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'complete'
);
});
test('shows a loading callout when elastic agent is installing', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-install',
'loading'
);
await expect(
customLogsPage.stepStatusLoading.getByText(
CustomLogsPage.ASSERTION_MESSAGES.INSTALLING_AGENT_STATUS
)
).toBeVisible();
});
test('shows a success callout when elastic agent is installed', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-install',
'complete'
);
await expect(
customLogsPage.stepStatusComplete.getByText(
CustomLogsPage.ASSERTION_MESSAGES.INSTALLED_AGENT_STATUS
)
).toBeVisible();
});
test('shows a danger callout when elastic agent was not installed', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-install',
'danger'
);
await expect(
customLogsPage.stepStatusDanger.getByText(
CustomLogsPage.ASSERTION_MESSAGES.INSTALL_AGENT_DANGER_CALLOUT
)
).toBeVisible();
});
});
test.describe('Check elastic Agent status step', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-install',
'complete'
);
});
test('shows a loading callout when getting elastic agent status', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-status',
'loading'
);
await expect(
customLogsPage.stepStatusLoading.getByText(
CustomLogsPage.ASSERTION_MESSAGES.CONNECTING_TO_AGENT_STATUS
)
).toBeVisible();
});
test('shows a success callout when elastic agent status is healthy', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-status',
'complete',
{
agentId: 'test-agent-id',
}
);
await expect(
customLogsPage.stepStatusComplete.getByText(
CustomLogsPage.ASSERTION_MESSAGES.CONNECTED_TO_AGENT_STATUS
)
).toBeVisible();
});
test('shows a warning callout when elastic agent status is not healthy', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-status',
'warning'
);
await expect(
customLogsPage.stepStatusWarning.getByText(
CustomLogsPage.ASSERTION_MESSAGES.CONNECT_AGENT_WARNING_CALLOUT
)
).toBeVisible();
});
});
});
});
});
test.describe('Configure Elastic Agent step', () => {
let onboardingId: string;
test.beforeEach(setupPage({ isAdmin: true }));
test.beforeEach(async ({ page, pageObjects: { customLogsPage } }) => {
await page.route('**/observability_onboarding/logs/flow', async (route) => {
const response = await route.fetch();
const body = await response.json();
onboardingId = body.onboardingId;
await route.fulfill({ response });
});
await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible();
});
test.describe('When user select Linux OS', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.autoDownloadConfigurationToggle.click();
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
});
test('shows loading callout when config is being downloaded to the host', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading');
await expect(
customLogsPage.stepStatusLoading.getByText(
CustomLogsPage.ASSERTION_MESSAGES.DOWNLOAD_AGENT_CONFIG_STATUS
)
).toBeVisible();
});
test('shows success callout when the configuration has been written to the host', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
await expect(
customLogsPage.stepStatusComplete.getByText(
CustomLogsPage.ASSERTION_MESSAGES.AGENT_CONFIGURATION_SUCCESS_CALLOUT_LINUX
)
).toBeVisible();
});
test('shows warning callout when the configuration was not written in the host', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning');
await expect(
customLogsPage.stepStatusWarning.getByText(
CustomLogsPage.ASSERTION_MESSAGES.CONFIGURE_AGENT_WARNING_CALLOUT
)
).toBeVisible();
});
});
test.describe('When user select Mac OS', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.macOSCodeSnippetButton.click();
await customLogsPage.autoDownloadConfigurationToggle.click();
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-extract', 'complete');
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-install', 'complete');
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-status', 'complete', {
agentId: 'test-agent-id',
});
});
test('shows loading callout when config is being downloaded to the host', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'loading');
await expect(
customLogsPage.stepStatusLoading.getByText(
CustomLogsPage.ASSERTION_MESSAGES.DOWNLOADING_AGENT_CONFIG_STATUS
)
).toBeVisible();
});
test('shows success callout when the configuration has been written to the host', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'complete');
await expect(
customLogsPage.stepStatusComplete.getByText(
CustomLogsPage.ASSERTION_MESSAGES.AGENT_CONFIGURATION_SUCCESS_CALLOUT_MACOS
)
).toBeVisible();
});
test('shows warning callout when the configuration was not written in the host', async ({
pageObjects: { customLogsPage },
}) => {
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-config', 'warning');
await expect(
customLogsPage.stepStatusWarning.getByText(
CustomLogsPage.ASSERTION_MESSAGES.CONFIGURE_AGENT_WARNING_CALLOUT
)
).toBeVisible();
});
});
test.describe('When user select Windows', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
customLogsPage.windowsCodeSnippetButton.click();
});
test('step is disabled', async ({ pageObjects: { customLogsPage } }) => {
await expect(
customLogsPage.configureElasticAgentStep.getByText(
CustomLogsPage.ASSERTION_MESSAGES.INSTALLATION_STEP_2_DISABLED
)
).toBeVisible();
});
});
});
test.describe('Check logs step', () => {
let onboardingId: string;
test.beforeEach(setupPage({ isAdmin: true }));
test.beforeEach(async ({ page, pageObjects: { customLogsPage } }) => {
await page.route('**/observability_onboarding/logs/flow', async (route) => {
const response = await route.fetch();
const body = await response.json();
onboardingId = body.onboardingId;
await route.fulfill({ response });
});
await expect(customLogsPage.apiKeyCreateSuccessCallout).toBeVisible();
});
test.describe('When user select Linux OS or MacOS', () => {
test.describe('When configure Elastic Agent step is not finished', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-install',
'complete'
);
await customLogsPage.updateInstallationStepStatus(onboardingId, 'ea-status', 'loading');
});
test('check logs is not triggered', async ({ page }) => {
await expect(
page.locator(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-incomplete"]'
)
).toBeVisible();
await expect(
page.locator('.euiStep__title', { hasText: 'Ship logs to Elastic Observability' })
).toBeVisible();
});
});
test.describe('When configure Elastic Agent step has finished', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-download',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-extract',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-install',
'complete'
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-status',
'complete',
{
agentId: 'test-agent-id',
}
);
await customLogsPage.updateInstallationStepStatus(
onboardingId,
'ea-config',
'complete'
);
});
test('shows loading callout when logs are being checked', async ({ page }) => {
await expect(
page.locator(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-loading"]'
)
).toBeVisible();
await expect(
page.locator('.euiStep__title', { hasText: 'Waiting for logs to be shipped...' })
).toBeVisible();
});
});
});
test.describe('When user select Windows', () => {
test.beforeEach(async ({ pageObjects: { customLogsPage } }) => {
await customLogsPage.windowsCodeSnippetButton.click();
});
test('step is disabled', async ({ pageObjects: { customLogsPage }, page }) => {
await expect(
page.locator(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-disabled"]'
)
).toBeVisible();
});
});
});
test.describe('When logs are being shipped', () => {
test.beforeEach(async ({ page }) => {
await page.route('**/progress', (route) => {
route.fulfill({
status: 200,
body: JSON.stringify({
progress: {
'ea-download': { status: 'complete' },
'ea-extract': { status: 'complete' },
'ea-install': { status: 'complete' },
'ea-status': { status: 'complete' },
'ea-config': { status: 'complete' },
'logs-ingest': { status: 'complete' },
},
}),
});
});
});
test.beforeEach(setupPage({ isAdmin: true }));
test('shows success callout when logs have arrived to elastic', async ({ page }) => {
await expect(
page.locator(
'[data-test-subj="obltOnboardingCheckLogsStep"] .euiStep__titleWrapper [class$="euiStepNumber-s-complete"]'
)
).toBeVisible();
await expect(
page.locator('.euiStep__title', { hasText: 'Logs are being shipped!' })
).toBeVisible();
});
test('when user clicks on Explore Logs it navigates to observability Logs Explorer', async ({
page,
}) => {
await page.locator('[data-test-subj="obltOnboardingExploreLogs"]').click();
await expect(page).toHaveURL(/\/app\/observability-logs-explorer/);
});
});
}
);

View file

@ -0,0 +1,15 @@
{
"extends": "../../../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": [
"**/*"
],
"kbn_references": [
"@kbn/scout"
],
"exclude": [
"target/**/*"
]
}

View file

@ -6578,10 +6578,6 @@
version "0.0.0"
uid ""
"@kbn/observability-onboarding-e2e@link:x-pack/solutions/observability/plugins/observability_onboarding/e2e":
version "0.0.0"
uid ""
"@kbn/observability-onboarding-plugin@link:x-pack/solutions/observability/plugins/observability_onboarding":
version "0.0.0"
uid ""