From 9650da839fff9fee4b8323baee42fba1239e53f7 Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Fri, 23 May 2025 08:37:51 -0400 Subject: [PATCH] chore(streams): add scout test for wired and classic stream (#220295) --- .buildkite/scout_ci_config.yml | 1 + .../playwright/fixtures/worker/apis/index.ts | 3 + .../fixtures/worker/apis/streams/index.ts | 47 +++++++ .../stream_detail_lifecycle/metadata.tsx | 10 +- .../add_dashboard_flyout.tsx | 1 + .../dashboard_table.tsx | 5 +- .../stream_detail_dashboards_view/index.tsx | 1 + .../stream_list_view/empty_prompt.tsx | 1 + .../plugins/shared/streams_app/tsconfig.json | 6 +- .../shared/streams_app/ui_tests/README.md | 25 ++++ .../ui_tests/fixtures/constants.ts | 10 ++ .../streams_app/ui_tests/fixtures/index.ts | 31 +++++ .../ui_tests/fixtures/page_objects/index.ts | 20 +++ .../fixtures/page_objects/streams_app.ts | 61 +++++++++ .../streams_app/ui_tests/playwright.config.ts | 13 ++ .../ui_tests/tests/classic.spec.ts | 111 ++++++++++++++++ .../streams_app/ui_tests/tests/wired.spec.ts | 123 ++++++++++++++++++ .../kbn_archiver/dashboard/simple.json | 36 +++++ 18 files changed, 501 insertions(+), 4 deletions(-) create mode 100644 src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/streams/index.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/README.md create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/constants.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/index.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/index.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/streams_app.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/playwright.config.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/tests/classic.spec.ts create mode 100644 x-pack/platform/plugins/shared/streams_app/ui_tests/tests/wired.spec.ts create mode 100644 x-pack/test/functional/fixtures/kbn_archiver/dashboard/simple.json diff --git a/.buildkite/scout_ci_config.yml b/.buildkite/scout_ci_config.yml index f2b265033aee..e6e281cd3601 100644 --- a/.buildkite/scout_ci_config.yml +++ b/.buildkite/scout_ci_config.yml @@ -7,4 +7,5 @@ ui_tests: - observability_onboarding - painless_lab - security_solution + - streams_app disabled: diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/index.ts b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/index.ts index 888fe4fa0ce0..e100392334c3 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/index.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/index.ts @@ -9,9 +9,11 @@ import { coreWorkerFixtures } from '../core_fixtures'; import { FleetApiService, getFleetApiHelper } from './fleet'; +import { StreamsApiService, getStreamsApiService } from './streams'; export interface ApiServicesFixture { fleet: FleetApiService; + streams: StreamsApiService; // add more services here } @@ -26,6 +28,7 @@ export const apiServicesFixture = coreWorkerFixtures.extend< async ({ kbnClient, log }, use) => { const services = { fleet: getFleetApiHelper(log, kbnClient), + streams: getStreamsApiService({ kbnClient, log }), }; log.serviceLoaded('apiServices'); diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/streams/index.ts b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/streams/index.ts new file mode 100644 index 000000000000..813200301a30 --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/apis/streams/index.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { KbnClient, ScoutLogger, measurePerformanceAsync } from '../../../../../common'; +import { ScoutParallelWorkerFixtures } from '../../../parallel_run_fixtures'; + +export interface StreamsApiService { + enable: () => Promise; + disable: () => Promise; +} + +export const getStreamsApiService = ({ + kbnClient, + log, + scoutSpace, +}: { + kbnClient: KbnClient; + log: ScoutLogger; + scoutSpace?: ScoutParallelWorkerFixtures['scoutSpace']; +}): StreamsApiService => { + const basePath = scoutSpace?.id ? `/s/${scoutSpace?.id}` : ''; + + return { + enable: async () => { + await measurePerformanceAsync(log, 'streamsApi.enable', async () => { + await kbnClient.request({ + method: 'POST', + path: `${basePath}/api/streams/_enable`, + }); + }); + }, + disable: async () => { + await measurePerformanceAsync(log, 'streamsApi.disable', async () => { + await kbnClient.request({ + method: 'POST', + path: `${basePath}/api/streams/_disable`, + }); + }); + }, + }; +}; diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_lifecycle/metadata.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_lifecycle/metadata.tsx index 40dc10d01682..35bf7dfbb247 100644 --- a/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_lifecycle/metadata.tsx +++ b/x-pack/platform/plugins/shared/streams_app/public/components/data_management/stream_detail_lifecycle/metadata.tsx @@ -170,6 +170,7 @@ export function RetentionMetadata({ } button={contextualMenu} + dataTestSubj="streamsAppRetentionMetadataRetentionPeriod" /> + diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/add_dashboard_flyout.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/add_dashboard_flyout.tsx index b8490f6d4973..c65d054e432c 100644 --- a/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/add_dashboard_flyout.tsx +++ b/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/add_dashboard_flyout.tsx @@ -207,6 +207,7 @@ export function AddDashboardFlyout({ loading={dashboardSuggestionsFetch.loading} selectedDashboards={selectedDashboards} setSelectedDashboards={setSelectedDashboards} + dataTestSubj="streamsAppAddDashboardFlyoutDashboardsTable" /> diff --git a/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/dashboard_table.tsx b/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/dashboard_table.tsx index 9cf9193db635..a4252e408bf7 100644 --- a/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/dashboard_table.tsx +++ b/x-pack/platform/plugins/shared/streams_app/public/components/stream_detail_dashboards_view/dashboard_table.tsx @@ -27,6 +27,7 @@ export function DashboardsTable({ setSelectedDashboards, loading, entityId, + dataTestSubj, }: { entityId?: string; loading: boolean; @@ -34,6 +35,7 @@ export function DashboardsTable({ compact?: boolean; selectedDashboards?: SanitizedDashboardAsset[]; setSelectedDashboards?: (dashboards: SanitizedDashboardAsset[]) => void; + dataTestSubj?: string; }) { const { core: { application }, @@ -58,7 +60,7 @@ export function DashboardsTable({ }), render: (_, { title, id }) => ( { if (entityId) { telemetryClient.trackAssetClick({ @@ -117,6 +119,7 @@ export function DashboardsTable({ {definition && isAddDashboardFlyoutOpen ? ( } title={

diff --git a/x-pack/platform/plugins/shared/streams_app/tsconfig.json b/x-pack/platform/plugins/shared/streams_app/tsconfig.json index 6aad9554a413..4761b467c7cb 100644 --- a/x-pack/platform/plugins/shared/streams_app/tsconfig.json +++ b/x-pack/platform/plugins/shared/streams_app/tsconfig.json @@ -9,7 +9,8 @@ "typings/**/*", "public/**/*.json", "server/**/*", - ".storybook/**/*" + ".storybook/**/*", + "ui_tests/**/*" ], "exclude": ["target/**/*", ".storybook/**/*.js"], "kbn_references": [ @@ -68,6 +69,7 @@ "@kbn/ingest-pipelines-plugin", "@kbn/deeplinks-observability", "@kbn/content-packs-schema", - "@kbn/charts-theme" + "@kbn/charts-theme", + "@kbn/scout" ] } diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/README.md b/x-pack/platform/plugins/shared/streams_app/ui_tests/README.md new file mode 100644 index 000000000000..28e69e32baa1 --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/README.md @@ -0,0 +1,25 @@ +## 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: + +Some tests are designed to run sequentially: + +```bash +// ESS +npx playwright test --config x-pack/platform/plugins/shared/streams_app/ui_tests/playwright.config.ts --project=local --grep @ess + +// Serverless +npx playwright test --config x-pack/platform/plugins/shared/streams_app/ui_tests/playwright.config.ts --project=local --grep @svlOblt +``` + +Test results are available in `x-pack/platform/plugins/shared/streams_app/ui_tests/output` diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/constants.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/constants.ts new file mode 100644 index 000000000000..86465e5a01a6 --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/constants.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const KBN_ARCHIVES = { + DASHBOARD: 'x-pack/test/functional/fixtures/kbn_archiver/dashboard/simple.json', +}; diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/index.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/index.ts new file mode 100644 index 000000000000..5e9fc07e3ec7 --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/index.ts @@ -0,0 +1,31 @@ +/* + * 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, ScoutTestFixtures, ScoutWorkerFixtures, test as baseTest } from '@kbn/scout'; +import { StreamsPageObjects, extendPageObjects } from './page_objects'; + +export interface StreamsTestFixtures extends ScoutTestFixtures { + pageObjects: StreamsPageObjects; +} + +export const test = baseTest.extend({ + pageObjects: async ( + { + pageObjects, + page, + }: { + pageObjects: StreamsPageObjects; + page: ScoutPage; + }, + use: (pageObjects: StreamsPageObjects) => Promise + ) => { + const extendedPageObjects = extendPageObjects(pageObjects, page); + await use(extendedPageObjects); + }, +}); + +export * as testData from './constants'; diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/index.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/index.ts new file mode 100644 index 000000000000..7e619faafbad --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PageObjects, ScoutPage, createLazyPageObject } from '@kbn/scout'; +import { StreamsApp } from './streams_app'; + +export interface StreamsPageObjects extends PageObjects { + streams: StreamsApp; +} + +export function extendPageObjects(pageObjects: PageObjects, page: ScoutPage): StreamsPageObjects { + return { + ...pageObjects, + streams: createLazyPageObject(StreamsApp, page), + }; +} diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/streams_app.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/streams_app.ts new file mode 100644 index 000000000000..2fbcba51fcfa --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/fixtures/page_objects/streams_app.ts @@ -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 { ScoutPage, expect } from '@kbn/scout'; + +export class StreamsApp { + constructor(private readonly page: ScoutPage) {} + + async goto() { + await this.page.gotoApp('streams'); + await expect(this.page.getByText('StreamsTechnical Preview')).toBeVisible(); + } + + async gotoStreamsFromBreadcrumb() { + await this.page + .getByTestId('breadcrumbs') + .getByRole('link', { name: 'Streams', exact: true }) + .click(); + } + + async gotoStream(stream: string) { + const last = await this.page.getByTestId('breadcrumb last').textContent(); + if (last !== 'Streams') { + await this.gotoStreamsFromBreadcrumb(); + } + await this.page.getByRole('link', { name: stream, exact: true }).click(); + } + + async gotoStreamDashboard(stream: string) { + await this.gotoStream(stream); + await this.page.getByRole('tab', { name: 'Dashboards' }).click(); + } + + async gotoManageStream(stream: string) { + this.gotoStream(stream); + await this.page.getByRole('link', { name: 'Manage stream' }).click(); + } + + async gotoCreateChildStream(parent: string) { + await this.gotoManageStream(parent); + await this.page.getByRole('button', { name: 'Create child stream' }).click(); + } + + async gotoDataRetentionTab(stream: string) { + await this.gotoManageStream(stream); + await this.page.getByRole('tab', { name: 'Data retention' }).click(); + } + + async gotoExtractFieldTab(stream: string) { + await this.gotoManageStream(stream); + await this.page.getByRole('tab', { name: 'Extract field' }).click(); + } + + async gotoSchemaEditorTab(stream: string) { + await this.gotoManageStream(stream); + await this.page.getByRole('tab', { name: 'Schema editor' }).click(); + } +} diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/playwright.config.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/playwright.config.ts new file mode 100644 index 000000000000..34b370396b67 --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/playwright.config.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createPlaywrightConfig } from '@kbn/scout'; + +// eslint-disable-next-line import/no-default-export +export default createPlaywrightConfig({ + testDir: './tests', +}); diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/tests/classic.spec.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/tests/classic.spec.ts new file mode 100644 index 000000000000..9dae34272e65 --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/tests/classic.spec.ts @@ -0,0 +1,111 @@ +/* + * 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 { testData, test } from '../fixtures'; + +const DATA_STREAM_NAME = 'my-data-stream'; + +test.describe('Classic Streams', { tag: ['@ess', '@svlOblt'] }, () => { + test.beforeEach(async ({ kbnClient, esClient, browserAuth, pageObjects }) => { + await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD); + await esClient.indices.putIndexTemplate({ + name: 'my-index-template', + index_patterns: [`${DATA_STREAM_NAME}*`], + data_stream: {}, + priority: 500, + template: { + lifecycle: { + data_retention: '7d', + }, + }, + }); + + await esClient.indices.createDataStream({ + name: DATA_STREAM_NAME, + }); + + await esClient.index({ + index: DATA_STREAM_NAME, + document: { + '@timestamp': '2025-05-01T00:00:00.000Z', + message: 'GET /search HTTP/1.1 200 1070000', + 'agent.name': 'nginx', + }, + refresh: 'wait_for', + }); + await browserAuth.loginAsAdmin(); + await pageObjects.streams.goto(); + }); + + test.afterAll(async ({ kbnClient, esClient, apiServices }) => { + await esClient.indices.deleteDataStream({ name: DATA_STREAM_NAME }); + await esClient.indices.deleteIndexTemplate({ + name: 'my-index-template', + }); + await kbnClient.savedObjects.cleanStandardList(); + }); + + test('full flow', async ({ page, esClient, pageObjects }) => { + // Update data retention + await pageObjects.streams.gotoDataRetentionTab(DATA_STREAM_NAME); + + await page.getByRole('button', { name: 'Edit data retention' }).click(); + await page.getByRole('button', { name: 'Set specific retention days' }).click(); + await page.getByTestId('streamsAppDslModalDaysField').fill('30'); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByTestId('streamsAppRetentionMetadataRetentionPeriod').getByText('30d') + ).toBeVisible(); + await page.getByTestId('toastCloseButton').click(); + + // Update field extraction + await pageObjects.streams.gotoExtractFieldTab(DATA_STREAM_NAME); + await page.getByText('Add a processor').click(); + + await page.locator('input[name="field"]').fill('message'); + await page + .locator('input[name="patterns\\.0\\.value"]') + .fill('%{WORD:method} %{URIPATH:request}'); + await page.getByRole('button', { name: 'Add processor' }).click(); + await page.getByRole('button', { name: 'Save changes' }).click(); + + await expect(page.getByText("Stream's processors updated")).toBeVisible(); + await page.getByTestId('toastCloseButton').click(); + + // Add dashboard + await pageObjects.streams.gotoStreamDashboard(DATA_STREAM_NAME); + await page.getByRole('button', { name: 'Add a dashboard' }).click(); + await expect( + page + .getByTestId('streamsAppAddDashboardFlyoutDashboardsTable') + .getByRole('button', { name: 'Some Dashboard' }) + ).toBeVisible(); + // eslint-disable-next-line playwright/no-nth-methods + await page.getByRole('cell', { name: 'Select row' }).locator('div').first().click(); + await page.getByRole('button', { name: 'Add dashboard' }).click(); + await expect( + page + .getByTestId('streamsAppStreamDetailDashboardsTable') + .getByTestId('streamsAppDashboardColumnsLink') + ).toHaveText('Some Dashboard'); + + // remove dashboard + await page + .getByTestId('streamsAppStreamDetailDashboardsTable') + .getByRole('cell', { name: 'Select row' }) + .locator('div') + // eslint-disable-next-line playwright/no-nth-methods + .first() + .click(); + + await page.getByRole('button', { name: 'Unlink selected' }).click(); + await expect( + page.getByTestId('streamsAppStreamDetailDashboardsTable').getByText('No items found') + ).toBeVisible(); + }); +}); diff --git a/x-pack/platform/plugins/shared/streams_app/ui_tests/tests/wired.spec.ts b/x-pack/platform/plugins/shared/streams_app/ui_tests/tests/wired.spec.ts new file mode 100644 index 000000000000..b1e00764fecd --- /dev/null +++ b/x-pack/platform/plugins/shared/streams_app/ui_tests/tests/wired.spec.ts @@ -0,0 +1,123 @@ +/* + * 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 { testData, test } from '../fixtures'; + +test.describe('Wired Streams', { tag: ['@ess', '@svlOblt'] }, () => { + test.beforeEach(async ({ apiServices, kbnClient, browserAuth, pageObjects }) => { + await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD); + await apiServices.streams.enable(); + await browserAuth.loginAsAdmin(); + await pageObjects.streams.goto(); + }); + + test.afterAll(async ({ kbnClient, apiServices }) => { + await apiServices.streams.disable(); + await kbnClient.savedObjects.cleanStandardList(); + }); + + test('full flow', async ({ page, esClient, pageObjects }) => { + await pageObjects.streams.gotoCreateChildStream('logs'); + + await page.getByLabel('Stream name').fill('logs.nginx'); + await page.getByPlaceholder('Field').fill('agent.name'); + await page.getByPlaceholder('Value').fill('nginx'); + await page.getByRole('button', { name: 'Save' }).click(); + await expect(page.getByRole('link', { name: 'logs.nginx', exact: true })).toBeVisible(); + await page.getByTestId('toastCloseButton').click(); + + // Update "logs.nginx" data retention + await pageObjects.streams.gotoDataRetentionTab('logs.nginx'); + await page.getByRole('button', { name: 'Edit data retention' }).click(); + await page.getByRole('button', { name: 'Set specific retention days' }).click(); + await page.getByTestId('streamsAppDslModalDaysField').fill('7'); + await page.getByRole('button', { name: 'Save' }).click(); + await expect( + page.getByTestId('streamsAppRetentionMetadataRetentionPeriod').getByText('7d') + ).toBeVisible(); + await page.getByTestId('toastCloseButton').click(); + + // Update "logs.nginx" processing + await esClient.index({ + index: 'logs', + document: { + '@timestamp': '2025-05-01T00:00:00.000Z', + message: JSON.stringify({ + 'log.level': 'info', + 'log.logger': 'nginx', + message: 'GET /search HTTP/1.1 200 1070000', + }), + 'agent.name': 'nginx', + 'other.field': 'important', + }, + refresh: 'wait_for', + }); + + await pageObjects.streams.gotoExtractFieldTab('logs.nginx'); + await page.getByText('Add a processor').click(); + + await page.locator('input[name="field"]').fill('message'); + await page + .locator('input[name="patterns\\.0\\.value"]') + .fill('%{WORD:method} %{URIPATH:request}'); + await page.getByRole('button', { name: 'Add processor' }).click(); + await page.getByRole('button', { name: 'Save changes' }).click(); + await expect(page.getByText("Stream's processors updated")).toBeVisible(); + await page.getByTestId('toastCloseButton').click(); + + // Update "logs.nginx" mapping + await pageObjects.streams.gotoSchemaEditorTab('logs.nginx'); + + await page + .getByRole('row', { name: 'agent.name ----- ----- logs.' }) + .getByLabel('Open actions menu') + .click(); + await page + .getByRole('row', { name: 'agent.name ----- ----- logs.' }) + .getByLabel('Open actions menu') + .click(); + await page.getByRole('button', { name: 'Map field' }).click(); + await page.getByRole('combobox').selectOption('keyword'); + await page.getByRole('button', { name: 'Save changes' }).click(); + await page.getByRole('heading', { name: 'agent.name' }).waitFor({ state: 'hidden' }); + + await expect(page.getByText('Mapped', { exact: true })).toBeVisible(); + await page.getByTestId('toastCloseButton').click(); + + // Add dashboard + await pageObjects.streams.gotoStreamDashboard('logs.nginx'); + await page.getByRole('button', { name: 'Add a dashboard' }).click(); + await expect( + page + .getByTestId('streamsAppAddDashboardFlyoutDashboardsTable') + .getByRole('button', { name: 'Some Dashboard' }) + ).toBeVisible(); + // eslint-disable-next-line playwright/no-nth-methods + await page.getByRole('cell', { name: 'Select row' }).locator('div').first().click(); + await page.getByRole('button', { name: 'Add dashboard' }).click(); + await expect( + page + .getByTestId('streamsAppStreamDetailDashboardsTable') + .getByTestId('streamsAppDashboardColumnsLink') + ).toHaveText('Some Dashboard'); + + // remove dashboard + await page + .getByTestId('streamsAppStreamDetailDashboardsTable') + .getByRole('cell', { name: 'Select row' }) + .locator('div') + // eslint-disable-next-line playwright/no-nth-methods + .first() + .click(); + + await page.getByRole('button', { name: 'Unlink selected' }).click(); + await expect( + page.getByTestId('streamsAppStreamDetailDashboardsTable').getByText('No items found') + ).toBeVisible(); + }); +}); diff --git a/x-pack/test/functional/fixtures/kbn_archiver/dashboard/simple.json b/x-pack/test/functional/fixtures/kbn_archiver/dashboard/simple.json new file mode 100644 index 000000000000..eed166e117e0 --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/dashboard/simple.json @@ -0,0 +1,36 @@ +{ + "attributes": { + "controlGroupInput": { + "chainingSystem": "HIERARCHICAL", + "controlStyle": "oneLine", + "ignoreParentSettingsJSON": "{\"ignoreFilters\":false,\"ignoreQuery\":false,\"ignoreTimerange\":false,\"ignoreValidations\":false}", + "panelsJSON": "{}", + "showApplySelections": false + }, + "description": "Some dashboard", + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"kuery\"}}" + }, + "optionsJSON": "{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}", + "panelsJSON": "[]", + "timeRestore": false, + "title": "Some Dashboard", + "version": 3 + }, + "coreMigrationVersion": "8.8.0", + "created_at": "2025-05-09T14:33:00.927Z", + "created_by": "u_mGBROF_q5bmFCATbLXAcCwKa0k8JvONAwSruelyKA5E_0", + "id": "a1395240-bc20-4cc7-8559-349d049622d2", + "managed": false, + "references": [ + { + "id": "64b8958c-e914-48ff-a31d-fc51227093b8", + "name": "tag-ref-64b8958c-e914-48ff-a31d-fc51227093b8", + "type": "tag" + } + ], + "type": "dashboard", + "typeMigrationVersion": "10.2.0", + "updated_at": "2025-05-09T14:33:00.927Z", + "version": "WzE2LDFd" +}