-
Notifications
You must be signed in to change notification settings - Fork 8.6k
[Observability Onboarding] Add Wired Streams Ensemble e2e coverage #256265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
988e0cb
ee0d81f
c833c18
c9c951c
2a26d5c
7d9da7f
57af770
5f3c1cf
d0517e0
87b4166
2ba3c52
83831b1
94ee032
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| /* | ||
| * 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 } from '@playwright/test'; | ||
| import { DiscoverValidationPage } from '../stateful/pom/pages/discover_validation.page'; | ||
| import { StreamsValidationPage } from '../stateful/pom/pages/streams_validation.page'; | ||
|
|
||
| export async function assertDiscoverHasData( | ||
| page: Page, | ||
| { assertHitCount = false } = {} | ||
| ): Promise<void> { | ||
| const discoverValidation = new DiscoverValidationPage(page); | ||
| await discoverValidation.waitForDiscoverToLoad(); | ||
| await discoverValidation.assertHasAnyLogData(); | ||
| if (assertHitCount) { | ||
| await discoverValidation.assertHitCountGreaterThanZero(); | ||
| } | ||
| } | ||
|
|
||
| export async function assertStreamHasData(page: Page, streamName: string): Promise<void> { | ||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/streams`); | ||
| const streamsValidation = new StreamsValidationPage(page); | ||
| await streamsValidation.waitForStreamsToLoad(); | ||
| await streamsValidation.assertStreamDocCountGreaterThanZero(streamName); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,21 +10,32 @@ import path from 'node:path'; | |
| import { test } from './fixtures/base_page'; | ||
| import { HostDetailsPage } from './pom/pages/host_details.page'; | ||
| import { assertEnv } from '../lib/assert_env'; | ||
| import { assertDiscoverHasData, assertStreamHasData } from '../lib/validation_helpers'; | ||
|
|
||
| test.beforeEach(async ({ page }) => { | ||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/observabilityOnboarding`); | ||
| }); | ||
|
|
||
| test('Auto-detect logs and metrics', async ({ page, onboardingHomePage, autoDetectFlowPage }) => { | ||
| test('Auto-detect logs and metrics', async ({ | ||
| page, | ||
| onboardingHomePage, | ||
| autoDetectFlowPage, | ||
| wiredStreamsSelector, | ||
| }) => { | ||
| assertEnv(process.env.ARTIFACTS_FOLDER, 'ARTIFACTS_FOLDER is not defined.'); | ||
|
|
||
| const isLogsEssentialsMode = process.env.LOGS_ESSENTIALS_MODE === 'true'; | ||
| const useWiredStreams = process.env.USE_WIRED_STREAMS === 'true'; | ||
| const fileName = 'code_snippet_logs_auto_detect.sh'; | ||
| const outputPath = path.join(__dirname, '..', process.env.ARTIFACTS_FOLDER, fileName); | ||
|
|
||
| await onboardingHomePage.selectHostUseCase(); | ||
| await onboardingHomePage.selectAutoDetectWithElasticAgent(); | ||
|
|
||
| if (useWiredStreams) { | ||
| await wiredStreamsSelector.selectWiredStreamsMode(); | ||
| } | ||
|
|
||
| await autoDetectFlowPage.assertVisibilityCodeBlock(); | ||
| await autoDetectFlowPage.copyToClipboard(); | ||
|
|
||
|
|
@@ -36,7 +47,11 @@ test('Auto-detect logs and metrics', async ({ page, onboardingHomePage, autoDete | |
| */ | ||
| fs.writeFileSync(outputPath, clipboardData); | ||
|
|
||
| await autoDetectFlowPage.assertReceivedDataIndicator(); | ||
| if (useWiredStreams) { | ||
| await autoDetectFlowPage.assertLogsDataReceivedIndicator(); | ||
| } else { | ||
| await autoDetectFlowPage.assertReceivedDataIndicator(); | ||
| } | ||
|
|
||
| /** | ||
| * Host Details page sometime shows "No Data" | ||
|
|
@@ -50,24 +65,36 @@ test('Auto-detect logs and metrics', async ({ page, onboardingHomePage, autoDete | |
| */ | ||
| await page.waitForTimeout(2 * 60000); | ||
|
|
||
| await autoDetectFlowPage.clickAutoDetectSystemIntegrationCTA(); | ||
|
|
||
| /** | ||
| * Host Details pages open in a new tab, so it | ||
| * needs to be captured using the `popup` event. | ||
| * Wired streams only reroutes logs (to logs.ecs); metrics are unaffected. | ||
| * So for wired streams we validate log delivery via Discover and the Streams | ||
| * page, and intentionally skip the Host Details dashboard check. Dashboard | ||
| * validation is already covered by the non-wired test variants. | ||
| * | ||
| * Both "wired streams" and "wired streams + logs essentials" fall into this | ||
| * single branch because the validation path is identical for both. | ||
| */ | ||
| const hostDetailsPage = new HostDetailsPage(await page.waitForEvent('popup')); | ||
|
|
||
| if (!isLogsEssentialsMode) { | ||
| await hostDetailsPage.assertCpuPercentageNotEmpty(); | ||
| if (useWiredStreams) { | ||
| if (await autoDetectFlowPage.hasCustomLogsExploreButtons()) { | ||
| const popupPage = await autoDetectFlowPage.clickCustomLogsExploreInPopup(); | ||
| await assertDiscoverHasData(popupPage, { assertHitCount: true }); | ||
| } | ||
| await assertStreamHasData(page, 'logs.ecs'); | ||
| } else { | ||
| await autoDetectFlowPage.assertReceivedDataIndicator(); | ||
| await autoDetectFlowPage.clickAutoDetectSystemIntegrationCTA(); | ||
|
|
||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/discover`); | ||
| /** | ||
| * Host Details pages open in a new tab, so it | ||
| * needs to be captured using the `popup` event. | ||
| */ | ||
| const hostDetailsPage = new HostDetailsPage(await page.waitForEvent('popup')); | ||
|
Comment on lines
+84
to
+90
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n x-pack/solutions/observability/plugins/observability_onboarding/e2e/playwright/stateful/auto_detect.spec.ts | sed -n '75,110p'Repository: elastic/kibana Length of output: 1867 🌐 Web query:
💡 Result: In Playwright, Recommended (Playwright “event trap” pattern)const popupPromise = page.waitForEvent('popup'); // no await here
await page.getByText('open the popup').click();
const popup = await popupPromise;This is the pattern shown in the official docs. [1] Equivalent pattern with
|
||
|
|
||
| const { DiscoverValidationPage } = await import('./pom/pages/discover_validation.page'); | ||
| const discoverValidation = new DiscoverValidationPage(page); | ||
| await discoverValidation.waitForDiscoverToLoad(); | ||
| await discoverValidation.assertHasAnyLogData(); | ||
| if (!isLogsEssentialsMode) { | ||
| await hostDetailsPage.assertCpuPercentageNotEmpty(); | ||
| } else { | ||
| await autoDetectFlowPage.assertReceivedDataIndicator(); | ||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/discover`); | ||
| await assertDiscoverHasData(page); | ||
| } | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,15 +10,23 @@ import os from 'node:os'; | |
| import path from 'node:path'; | ||
| import { test } from './fixtures/base_page'; | ||
| import { assertEnv } from '../lib/assert_env'; | ||
| import { assertDiscoverHasData, assertStreamHasData } from '../lib/validation_helpers'; | ||
|
|
||
| test.beforeEach(async ({ page }) => { | ||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/observabilityOnboarding`); | ||
| }); | ||
|
|
||
| test('Otel Host', async ({ page, onboardingHomePage, otelHostFlowPage, hostsOverviewPage }) => { | ||
| test('Otel Host', async ({ | ||
| page, | ||
| onboardingHomePage, | ||
| otelHostFlowPage, | ||
| hostsOverviewPage, | ||
| wiredStreamsSelector, | ||
| }) => { | ||
| assertEnv(process.env.ARTIFACTS_FOLDER, 'ARTIFACTS_FOLDER is not defined.'); | ||
|
|
||
| const isLogsEssentialsMode = process.env.LOGS_ESSENTIALS_MODE === 'true'; | ||
| const useWiredStreams = process.env.USE_WIRED_STREAMS === 'true'; | ||
| const fileName = 'code_snippet_otel_host.sh'; | ||
| const outputPath = path.join(__dirname, '..', process.env.ARTIFACTS_FOLDER, fileName); | ||
|
|
||
|
|
@@ -28,6 +36,10 @@ test('Otel Host', async ({ page, onboardingHomePage, otelHostFlowPage, hostsOver | |
| const osName = process.env.OS_NAME || os.platform(); | ||
| await otelHostFlowPage.selectPlatform(osName); | ||
|
|
||
| if (useWiredStreams) { | ||
| await wiredStreamsSelector.selectWiredStreamsMode(); | ||
| } | ||
|
|
||
| await otelHostFlowPage.copyCollectorDownloadSnippetToClipboard(); | ||
| const collectorDownloadSnippet = (await page.evaluate( | ||
| 'navigator.clipboard.readText()' | ||
|
|
@@ -52,16 +64,26 @@ test('Otel Host', async ({ page, onboardingHomePage, otelHostFlowPage, hostsOver | |
| */ | ||
| await page.waitForTimeout(3 * 60000); | ||
|
|
||
| if (!isLogsEssentialsMode) { | ||
| /** | ||
| * Wired streams only reroutes logs (to logs.otel); metrics and traces are | ||
| * unaffected. So for wired streams we validate log delivery via Discover and | ||
| * the Streams page, and intentionally skip the Hosts Overview dashboard | ||
| * check. Dashboard validation is already covered by the non-wired test | ||
| * variants. | ||
| * | ||
| * Both "wired streams" and "wired streams + logs essentials" fall into this | ||
| * single branch because the validation path is identical for both. | ||
| */ | ||
| if (useWiredStreams) { | ||
| await otelHostFlowPage.clickLogsExplorationCTA(); | ||
| await assertDiscoverHasData(page, { assertHitCount: true }); | ||
| await assertStreamHasData(page, 'logs.otel'); | ||
| } else if (!isLogsEssentialsMode) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh, actually what about logs essentials + wired streams?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, had to quadruple check that. That also means we don't check any dashboards in wired streams flows in those tests. Initially I had a thought that we don't need to do this cause we don't touch metrics when enabling wired streams but now I am thinking that maybe it's also worth checking just to be safe.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, let's leave a comment though since these relationships are hard to figure out later on. |
||
| await otelHostFlowPage.clickHostsOverviewCTA(); | ||
| const hostname = os.hostname(); | ||
| await hostsOverviewPage.assertHostCpuNotEmpty(hostname); | ||
| } else { | ||
| await otelHostFlowPage.clickLogsExplorationCTA(); | ||
|
|
||
| const { DiscoverValidationPage } = await import('./pom/pages/discover_validation.page'); | ||
| const discoverValidation = new DiscoverValidationPage(page); | ||
| await discoverValidation.waitForDiscoverToLoad(); | ||
| await discoverValidation.assertHasAnyLogData(); | ||
| await assertDiscoverHasData(page); | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ import fs from 'node:fs'; | |
| import path from 'node:path'; | ||
| import { test } from './fixtures/base_page'; | ||
| import { assertEnv } from '../lib/assert_env'; | ||
| import { assertDiscoverHasData, assertStreamHasData } from '../lib/validation_helpers'; | ||
|
|
||
| test.beforeEach(async ({ page }) => { | ||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/observabilityOnboarding`); | ||
|
|
@@ -19,16 +20,22 @@ test('Kubernetes EA', async ({ | |
| onboardingHomePage, | ||
| kubernetesEAFlowPage, | ||
| kubernetesOverviewDashboardPage, | ||
| wiredStreamsSelector, | ||
| }) => { | ||
| assertEnv(process.env.ARTIFACTS_FOLDER, 'ARTIFACTS_FOLDER is not defined.'); | ||
|
|
||
| const isLogsEssentialsMode = process.env.LOGS_ESSENTIALS_MODE === 'true'; | ||
| const useWiredStreams = process.env.USE_WIRED_STREAMS === 'true'; | ||
| const fileName = 'code_snippet_kubernetes.sh'; | ||
| const outputPath = path.join(__dirname, '..', process.env.ARTIFACTS_FOLDER, fileName); | ||
|
|
||
| await onboardingHomePage.selectKubernetesUseCase(); | ||
| await onboardingHomePage.selectKubernetesQuickstart(); | ||
|
|
||
| if (useWiredStreams) { | ||
| await wiredStreamsSelector.selectWiredStreamsMode(); | ||
| } | ||
|
|
||
| await kubernetesEAFlowPage.assertVisibilityCodeBlock(); | ||
| await kubernetesEAFlowPage.copyToClipboard(); | ||
|
|
||
|
|
@@ -45,9 +52,30 @@ test('Kubernetes EA', async ({ | |
| */ | ||
| fs.writeFileSync(outputPath, clipboardData); | ||
|
|
||
| if (!isLogsEssentialsMode) { | ||
| /** | ||
| * Wired streams only reroutes logs (to logs.ecs); metrics are unaffected. | ||
| * So for wired streams we validate log delivery via Discover and the Streams | ||
| * page, and intentionally skip the Kubernetes Overview dashboard check. | ||
| * Dashboard validation is already covered by the non-wired test variants. | ||
| * | ||
| * The logs essentials sub-case skips the data indicator and navigates to | ||
| * Discover directly because the K8s EA has-data API relies on | ||
| * fields.onboarding_id, which is unreliable when metrics are disabled in | ||
| * the logs essentials tier. | ||
| */ | ||
| if (useWiredStreams && !isLogsEssentialsMode) { | ||
| await kubernetesEAFlowPage.assertReceivedDataIndicatorKubernetes(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a bit worried about how big these branches are getting - can we somehow deduplicate? It's fine if not, but it seems like it's easy to forget about stuff this way
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll try to extract some code into helpers |
||
| await page.waitForTimeout(2 * 60000); | ||
| await kubernetesEAFlowPage.clickExploreLogsCTA(); | ||
| await assertDiscoverHasData(page, { assertHitCount: true }); | ||
| await assertStreamHasData(page, 'logs.ecs'); | ||
| } else if (useWiredStreams && isLogsEssentialsMode) { | ||
| await page.waitForTimeout(5 * 60000); | ||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/discover`); | ||
| await assertDiscoverHasData(page, { assertHitCount: true }); | ||
| await assertStreamHasData(page, 'logs.ecs'); | ||
| } else if (!isLogsEssentialsMode) { | ||
| await kubernetesEAFlowPage.assertReceivedDataIndicatorKubernetes(); | ||
|
|
||
| /** | ||
| * There might be a case that dashboard still does not show | ||
| * the data even though it was ingested already. This usually | ||
|
|
@@ -58,17 +86,11 @@ test('Kubernetes EA', async ({ | |
| * for the data to propagate everywhere. | ||
| */ | ||
| await page.waitForTimeout(2 * 60000); | ||
|
|
||
| await kubernetesEAFlowPage.clickKubernetesAgentCTA(); | ||
| await kubernetesOverviewDashboardPage.assertNodesPanelNotEmpty(); | ||
| } else { | ||
| await page.waitForTimeout(5 * 60000); | ||
|
|
||
| await page.goto(`${process.env.KIBANA_BASE_URL}/app/discover`); | ||
|
|
||
| const { DiscoverValidationPage } = await import('./pom/pages/discover_validation.page'); | ||
| const discoverValidation = new DiscoverValidationPage(page); | ||
| await discoverValidation.waitForDiscoverToLoad(); | ||
| await discoverValidation.assertHasAnyLogData(); | ||
| await assertDiscoverHasData(page); | ||
| } | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ import { test } from './fixtures/base_page'; | |
| import { assertEnv } from '../lib/assert_env'; | ||
| import { OtelKubernetesOverviewDashboardPage } from './pom/pages/otel_kubernetes_overview_dashboard.page'; | ||
| import { ApmServiceInventoryPage } from './pom/pages/apm_service_inventory.page'; | ||
| import { assertDiscoverHasData, assertStreamHasData } from '../lib/validation_helpers'; | ||
|
|
||
| /** | ||
| * In case you need to run this test locally, you can use https://github.com/elastic/oblt-reference-stack | ||
|
|
@@ -29,10 +30,16 @@ test.beforeEach(async ({ page }) => { | |
| const INSTRUMENTED_APP_CONTAINER_NAMESPACE = 'java'; | ||
| const INSTRUMENTED_APP_NAME = 'java-app'; | ||
|
|
||
| test('Otel Kubernetes', async ({ page, onboardingHomePage, otelKubernetesFlowPage }) => { | ||
| test('Otel Kubernetes', async ({ | ||
| page, | ||
| onboardingHomePage, | ||
| otelKubernetesFlowPage, | ||
| wiredStreamsSelector, | ||
| }) => { | ||
| assertEnv(process.env.ARTIFACTS_FOLDER, 'ARTIFACTS_FOLDER is not defined.'); | ||
|
|
||
| const isLogsEssentialsMode = process.env.LOGS_ESSENTIALS_MODE === 'true'; | ||
| const useWiredStreams = process.env.USE_WIRED_STREAMS === 'true'; | ||
| const fileName = 'code_snippet_otel_kubernetes.sh'; | ||
| const outputPath = path.join(__dirname, '..', process.env.ARTIFACTS_FOLDER, fileName); | ||
|
|
||
|
|
@@ -42,12 +49,16 @@ test('Otel Kubernetes', async ({ page, onboardingHomePage, otelKubernetesFlowPag | |
| await otelKubernetesFlowPage.copyHelmRepositorySnippetToClipboard(); | ||
| const helmRepoSnippet = (await page.evaluate('navigator.clipboard.readText()')) as string; | ||
|
|
||
| if (useWiredStreams) { | ||
| await wiredStreamsSelector.selectWiredStreamsMode(); | ||
| } | ||
|
Comment on lines
49
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switch to wired mode before reading the first snippet. In wired runs, 💡 Suggested change- await otelKubernetesFlowPage.copyHelmRepositorySnippetToClipboard();
- const helmRepoSnippet = (await page.evaluate('navigator.clipboard.readText()')) as string;
-
if (useWiredStreams) {
await wiredStreamsSelector.selectWiredStreamsMode();
}
+
+ await otelKubernetesFlowPage.copyHelmRepositorySnippetToClipboard();
+ const helmRepoSnippet = (await page.evaluate('navigator.clipboard.readText()')) as string;
await otelKubernetesFlowPage.copyInstallStackSnippetToClipboard();🤖 Prompt for AI Agents |
||
|
|
||
| await otelKubernetesFlowPage.copyInstallStackSnippetToClipboard(); | ||
| const installStackSnippet = (await page.evaluate('navigator.clipboard.readText()')) as string; | ||
|
|
||
| let codeSnippet: string; | ||
|
|
||
| if (!isLogsEssentialsMode) { | ||
| if (!isLogsEssentialsMode && !useWiredStreams) { | ||
| /** | ||
| * Getting the snippets and replacing placeholder | ||
| * with the values used by Ensemble | ||
|
|
@@ -85,7 +96,21 @@ test('Otel Kubernetes', async ({ page, onboardingHomePage, otelKubernetesFlowPag | |
| */ | ||
| await page.waitForTimeout(5 * 60000); | ||
|
|
||
| if (!isLogsEssentialsMode) { | ||
| /** | ||
| * Wired streams only reroutes logs (to logs.otel); metrics and traces are | ||
| * unaffected. So for wired streams we validate log delivery via Discover and | ||
| * the Streams page, and intentionally skip the Cluster Overview dashboard | ||
| * and APM Service Inventory checks. Dashboard/APM validation is already | ||
| * covered by the non-wired test variants. | ||
| * | ||
| * Both "wired streams" and "wired streams + logs essentials" fall into this | ||
| * single branch because the validation path is identical for both. | ||
| */ | ||
| if (useWiredStreams) { | ||
| await otelKubernetesFlowPage.clickExploreLogsCTA(); | ||
| await assertDiscoverHasData(page, { assertHitCount: true }); | ||
| await assertStreamHasData(page, 'logs.otel'); | ||
| } else if (!isLogsEssentialsMode) { | ||
| const otelKubernetesOverviewDashboardPage = new OtelKubernetesOverviewDashboardPage( | ||
| await otelKubernetesFlowPage.openClusterOverviewDashboardInNewTab() | ||
| ); | ||
|
|
@@ -101,9 +126,7 @@ test('Otel Kubernetes', async ({ page, onboardingHomePage, otelKubernetesFlowPag | |
| await apmServiceInventoryPage.page.getByTestId(serviceTestId).click(); | ||
| await apmServiceInventoryPage.assertTransactionExists(); | ||
| } else { | ||
| const discoverValidation = | ||
| await otelKubernetesFlowPage.clickExploreLogsAndGetDiscoverValidation(); | ||
| await discoverValidation.waitForDiscoverToLoad(); | ||
| await discoverValidation.assertHasAnyLogData(); | ||
| await otelKubernetesFlowPage.clickExploreLogsCTA(); | ||
| await assertDiscoverHasData(page); | ||
| } | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't let wired-mode Discover coverage be skipped silently.
If
hasCustomLogsExploreButtons()flips tofalse, this branch still passes after only checking the stream page. Please assert those buttons are present or fall back to the non-popup Explore Logs action so regressions in the user-facing Discover path are caught.🤖 Prompt for AI Agents