[Obs Onboarding] Add E2E Ensemble test for Firehose flow (#223560)

Closes https://github.com/elastic/kibana/issues/208328

This change adds Playwright test for Ensemble e2e testing CI.

[🔒 Corresponding PR in Ensemble
repo](https://github.com/elastic/ensemble/pull/580)
[🔒 Successful CI
run](1561308352)

# How to test

1. Run Kibana locally
2. Adjust
`x-pack/solutions/observability/plugins/observability_onboarding/e2e/playwright/.env`
file if needed (see README in the same folder for more details)
3. Run the test:
```bash
npx playwright test -c ./x-pack/solutions/observability/plugins/observability_onboarding/e2e/playwright/playwright.config.ts --reporter list --headed x-pack/solutions/observability/plugins/observability_onboarding/e2e/playwright/stateful/firehose.spec.ts
```
4. The test will open the Firehose onboarding page and stop after
copying the CLI command snippet. When running on the CI, Ensemble would
take over by running the snippet on the CI instance. The Playwright test
at this point is just waiting for data to start coming in, to imitate
that without actually creating infrastructure on a real AWS account, you
can ingest a dummy document manually, for example:
```
# Adjust the @timestamp before executing
POST logs-aws.apigateway_logs-default/_doc
{
  "@timestamp": "2025-06-13T13:32:01.000Z",
  "some": 111,
  "aws.kinesis.name": "Elastic-Cloudwatch"
}
```
5. After doing that, you should see Kibana UI update with detected data
and the Playwright should continue and finish successfully.
This commit is contained in:
Mykola Harmash 2025-06-27 14:16:35 +02:00 committed by GitHub
parent 3da35797cd
commit f8e4ccb03b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 124 additions and 11 deletions

View file

@ -72,7 +72,7 @@ export default defineConfig({
testMatch: '*stateful/*.spec.ts',
use: {
...devices['Desktop Chrome'],
viewport: { width: 1920, height: 1200 },
viewport: { width: 1920, height: 2400 },
storageState: STORAGE_STATE,
launchOptions: {
logger: {

View file

@ -0,0 +1,61 @@
/*
* 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 fs from 'node:fs';
import path from 'node:path';
import { expect } from '@playwright/test';
import { test } from './fixtures/base_page';
import { assertEnv } from '../lib/assert_env';
test.beforeEach(async ({ page }) => {
await page.goto(`${process.env.KIBANA_BASE_URL}/app/observabilityOnboarding`);
});
test('Firehose', async ({ page, onboardingHomePage, firehoseFlowPage }) => {
assertEnv(process.env.ARTIFACTS_FOLDER, 'ARTIFACTS_FOLDER is not defined.');
assertEnv(process.env.ELASTICSEARCH_HOST, 'ELASTICSEARCH_HOST is not defined.');
const fileName = 'code_snippet_firehose.sh';
const outputPath = path.join(__dirname, '..', process.env.ARTIFACTS_FOLDER, fileName);
await onboardingHomePage.useCaseCloud.click();
await onboardingHomePage.awsCollectionCard.click();
await onboardingHomePage.firehoseQuickstartCard.click();
await firehoseFlowPage.cliOptionButton.click();
await firehoseFlowPage.copyToClipboardButton.click();
const snippet = (await page.evaluate('navigator.clipboard.readText()')) as string;
/**
* Ensemble story watches for the code snippet file
* to be created and then executes it
*/
fs.writeFileSync(outputPath, snippet);
/**
* The page waits for the browser window to loose
* focus as a signal to start checking for incoming data
*/
await page.evaluate('window.dispatchEvent(new Event("blur"))');
await expect(
firehoseFlowPage.waitingForDataIndicator,
'"Waiting for data" indicator should be visible'
).toBeVisible();
/**
* 5 minutes should be enough for the cloudformation
* stack to be created and to start pushing data.
*/
await page.waitForTimeout(5 * 60000);
await expect(
firehoseFlowPage.awsServiceReceivedDataItemList.first(),
'At least one AWS service should be detected'
).toBeVisible();
});

View file

@ -16,6 +16,7 @@ import { OtelKubernetesFlowPage } from '../pom/pages/otel_kubernetes_flow.page';
import { OtelKubernetesOverviewDashboardPage } from '../pom/pages/otel_kubernetes_overview_dashboard.page';
import { OtelHostFlowPage } from '../pom/pages/otel_host_flow.page';
import { HostsOverviewPage } from '../pom/pages/hosts_overview.page';
import { FirehoseFlowPage } from '../pom/pages/firehose_flow.page';
export const test = base.extend<{
headerBar: HeaderBar;
@ -28,6 +29,7 @@ export const test = base.extend<{
otelKubernetesOverviewDashboardPage: OtelKubernetesOverviewDashboardPage;
otelHostFlowPage: OtelHostFlowPage;
hostsOverviewPage: HostsOverviewPage;
firehoseFlowPage: FirehoseFlowPage;
}>({
headerBar: async ({ page }, use) => {
await use(new HeaderBar(page));
@ -68,4 +70,8 @@ export const test = base.extend<{
hostsOverviewPage: async ({ page }, use) => {
await use(new HostsOverviewPage(page));
},
firehoseFlowPage: async ({ page }, use) => {
await use(new FirehoseFlowPage(page));
},
});

View file

@ -0,0 +1,32 @@
/*
* 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 { type Page, type Locator } from '@playwright/test';
export class FirehoseFlowPage {
page: Page;
readonly cliOptionButton: Locator;
readonly copyToClipboardButton: Locator;
readonly waitingForDataIndicator: Locator;
readonly awsServiceReceivedDataItemList: Locator;
constructor(page: Page) {
this.page = page;
this.cliOptionButton = this.page.getByTestId('createCloudFormationOptionAWSCLI');
this.copyToClipboardButton = this.page.getByTestId(
'observabilityOnboardingCopyToClipboardButton'
);
this.waitingForDataIndicator = this.page.getByTestId(
'observabilityOnboardingFirehoseProgressCallout'
);
this.awsServiceReceivedDataItemList = this.page.getByTestId(
/^observabilityOnboardingAWSService-.+/
);
}
}

View file

@ -10,28 +10,42 @@ import type { Page, Locator } from '@playwright/test';
export class OnboardingHomePage {
page: Page;
private readonly otelKubernetesQuickStartCard: Locator;
private readonly useCaseKubernetes: Locator;
private readonly kubernetesQuickStartCard: Locator;
private readonly useCaseHost: Locator;
private readonly useCaseKubernetes: Locator;
readonly useCaseCloud: Locator;
private readonly otelKubernetesQuickStartCard: Locator;
private readonly kubernetesQuickStartCard: Locator;
private readonly autoDetectElasticAgent: Locator;
private readonly otelHostCard: Locator;
readonly awsCollectionCard: Locator;
readonly firehoseQuickstartCard: Locator;
constructor(page: Page) {
this.page = page;
this.otelKubernetesQuickStartCard = this.page.getByTestId('integration-card:otel-kubernetes');
this.useCaseKubernetes = this.page
.getByTestId('observabilityOnboardingUseCaseCard-kubernetes')
.getByRole('radio');
this.kubernetesQuickStartCard = this.page.getByTestId(
'integration-card:kubernetes-quick-start'
);
this.useCaseHost = this.page
.getByTestId('observabilityOnboardingUseCaseCard-host')
.getByRole('radio');
this.useCaseKubernetes = this.page
.getByTestId('observabilityOnboardingUseCaseCard-kubernetes')
.getByRole('radio');
this.useCaseCloud = this.page
.getByTestId('observabilityOnboardingUseCaseCard-cloud')
.getByRole('radio');
this.otelKubernetesQuickStartCard = this.page.getByTestId('integration-card:otel-kubernetes');
this.kubernetesQuickStartCard = this.page.getByTestId(
'integration-card:kubernetes-quick-start'
);
this.autoDetectElasticAgent = this.page.getByTestId('integration-card:auto-detect-logs');
this.otelHostCard = this.page.getByTestId('integration-card:otel-logs');
this.awsCollectionCard = this.page.getByTestId('integration-card:aws-logs-virtual');
this.firehoseQuickstartCard = this.page.getByTestId('integration-card:firehose-quick-start');
}
public async selectHostUseCase() {