From f90f53714312c93ee511d938f658f1c105a62f43 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 25 Mar 2026 12:02:56 -0700 Subject: [PATCH 1/4] Revert "[Discover][Scout] Fix flyout tests related to new scoped history" This reverts commit ed8273b2dcc0c6db67c775406f176b27174b48b2. --- .../src/playwright/page_objects/overlays.ts | 9 --------- .../traces_experience/page_objects/flyout.ts | 4 ---- .../flyout_stability/flyout_emotion_cache.spec.ts | 12 ++++-------- .../traces_experience/overview_tab_usage.spec.ts | 4 ++-- 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/page_objects/overlays.ts b/src/platform/packages/shared/kbn-scout/src/playwright/page_objects/overlays.ts index 005c45f29f3605..cbac75555c71bf 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/page_objects/overlays.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/page_objects/overlays.ts @@ -31,10 +31,6 @@ export class OverlaysPage { .locator('[data-test-subj="euiFlyoutCloseButton"]'); } - public get docViewerFlyoutCloseButton() { - return this.docViewerFlyout.locator('[data-test-subj="euiFlyoutCloseButton"]'); - } - public async openNewsfeedFlyout() { await this.newsfeedButton.click(); await expect(this.newsfeedFlyout).toBeVisible(); @@ -44,9 +40,4 @@ export class OverlaysPage { await this.newsfeedFlyoutCloseButton.click(); await expect(this.newsfeedFlyout).toBeHidden(); } - - public async closeDocViewerFlyout() { - await this.docViewerFlyoutCloseButton.click(); - await expect(this.docViewerFlyout).toBeHidden(); - } } diff --git a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/flyout.ts b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/flyout.ts index 503a7f4e85b13a..114afe1a1b161a 100644 --- a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/flyout.ts +++ b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/flyout.ts @@ -50,10 +50,7 @@ export interface TracesFlyout { readonly waterfallFlyout: { readonly container: Locator; - /** Present when managed flyout history has entries (e.g. after opening a nested doc flyout). */ readonly backButton: Locator; - /** Always present on managed flyouts; use to dismiss the timeline when Back is hidden (EUI 114+ scoped history). */ - readonly closeButton: Locator; getWaterfallItem(name: string): { readonly row: Locator; readonly content: Locator; @@ -135,7 +132,6 @@ export function createTracesFlyout(page: ScoutPage): TracesFlyout { return { container: timelineFlyout, backButton: timelineFlyout.locator('[data-test-subj="euiFlyoutMenuBackButton"]'), - closeButton: timelineFlyout.locator('[data-test-subj="euiFlyoutCloseButton"]'), getWaterfallItem(name: string) { const row = timelineFlyout .locator('[data-test-subj="traceItemRowWrapper"]') diff --git a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts index 094e51ede888d4..535a2e691f60ad 100644 --- a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts +++ b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts @@ -45,14 +45,10 @@ spaceTest.describe('Flyout system Emotion cache stability', { tag: tags.stateful await pageObjects.overlays.openNewsfeedFlyout(); }); - await spaceTest.step( - 'close stacked flyouts (EUI scoped history: closing one group does not close others)', - async () => { - await pageObjects.overlays.closeNewsfeedFlyout(); - await expect(pageObjects.overlays.docViewerFlyout).toBeVisible(); - await pageObjects.overlays.closeDocViewerFlyout(); - } - ); + await spaceTest.step('close the second flyout, triggering a cascade close', async () => { + await pageObjects.overlays.closeNewsfeedFlyout(); + await expect(pageObjects.overlays.docViewerFlyout).toBeHidden(); + }); await spaceTest.step('open a new flyout after the cascade close', async () => { await pageObjects.discover.openDocumentDetails({ rowIndex: 0 }); diff --git a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/overview_tab_usage.spec.ts b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/overview_tab_usage.spec.ts index 04701d78eeae84..3fb29184b6351e 100644 --- a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/overview_tab_usage.spec.ts +++ b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/overview_tab_usage.spec.ts @@ -166,7 +166,7 @@ spaceTest.describe( async () => { await flyout.traceSummary.waterfallClickArea.click(); await expect(flyout.waterfallFlyout.container).toBeVisible(); - await flyout.waterfallFlyout.closeButton.click(); + await flyout.waterfallFlyout.backButton.click(); await expect(flyout.waterfallFlyout.container).toBeHidden(); } ); @@ -176,7 +176,7 @@ spaceTest.describe( async () => { await flyout.traceSummary.fullScreenButton.click(); await expect(flyout.waterfallFlyout.container).toBeVisible(); - await flyout.waterfallFlyout.closeButton.click(); + await flyout.waterfallFlyout.backButton.click(); await expect(flyout.waterfallFlyout.container).toBeHidden(); } ); From 4f3c23a553cccc4419e9b0514f3c3a72687295cd Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 25 Mar 2026 12:42:56 -0700 Subject: [PATCH 2/4] wiring of historyKey for Traces Waterfall flyout --- .../doc_viewer_flyout/doc_viewer_flyout.tsx | 160 ++++++++++-------- .../flyout_history_key_context.ts | 30 ++++ .../full_screen_waterfall/index.test.tsx | 33 ++-- .../full_screen_waterfall/index.tsx | 3 + 4 files changed, 138 insertions(+), 88 deletions(-) create mode 100644 src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/flyout_history_key_context.ts diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx index c0f61cec503c05..77561a47cf6257 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/doc_viewer_flyout.tsx @@ -32,6 +32,7 @@ import useObservable from 'react-use/lib/useObservable'; import type { ChromeStart } from '@kbn/core/public'; import type { DocViewFilterFn, DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import type { DocViewerProps } from '@kbn/unified-doc-viewer'; +import { FlyoutHistoryKeyContext } from './flyout_history_key_context'; import { UnifiedDocViewer } from '../lazy_doc_viewer'; import { useFlyoutA11y } from './use_flyout_a11y'; @@ -253,84 +254,93 @@ export function UnifiedDocViewerFlyout({ const currentFlyoutTitle = flyoutTitle ?? defaultFlyoutTitle; const { a11yProps, screenReaderDescription } = useFlyoutA11y({ isXlScreen }); + // Shared history key for the EUI flyout manager: this symbol groups the + // Document Viewer flyout and any nested flyouts (e.g. Trace Waterfall) into + // the same back-button navigation history, enabling "Back" to return the user + // from the Trace Waterfall to the Document Viewer. + const historyKey = useMemo(() => Symbol('docViewerFlyout'), []); + return ( - - - {screenReaderDescription} - {renderSubheader && ( - <> - - {activePage !== -1 && ( - - - - )} - - {isEsqlQuery || !flyoutActions ? null : <>{flyoutActions}} - - - - - )} - - {renderCustomHeader && ( + + + + {screenReaderDescription} + {renderSubheader && ( <> - {renderCustomHeader(docViewRenderProps)} - + + {activePage !== -1 && ( + + + + )} + + {isEsqlQuery || !flyoutActions ? null : <>{flyoutActions}} + + + )} - - - - + + {renderCustomHeader && ( + <> + {renderCustomHeader(docViewRenderProps)} + + + )} + + + + + ); } diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/flyout_history_key_context.ts b/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/flyout_history_key_context.ts new file mode 100644 index 00000000000000..443a2c635151e7 --- /dev/null +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/doc_viewer_flyout/flyout_history_key_context.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", 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 { createContext, useContext } from 'react'; + +/** + * Provides the EUI flyout `historyKey` symbol shared between the Document Viewer + * flyout and any nested flyouts (e.g. Trace Waterfall) that should participate + * in the same back-button navigation history group. + * + * The context is provided by `UnifiedDocViewerFlyout` and consumed by + * `FullScreenWaterfall` so that the two flyouts share a history stack, + * enabling the "Back" button on the Trace Waterfall to return the user to + * the Document Viewer. + */ +export const FlyoutHistoryKeyContext = createContext(null); + +export const useFlyoutHistoryKey = () => { + const context = useContext(FlyoutHistoryKeyContext); + if (context === null) { + throw new Error('useFlyoutHistoryKey must be used within a FlyoutHistoryKeyContext.Provider'); + } + return context; +}; diff --git a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.test.tsx b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.test.tsx index 5d03cd566ed376..abbaf94bf3bc21 100644 --- a/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.test.tsx +++ b/src/platform/plugins/shared/unified_doc_viewer/public/components/observability/traces/components/full_screen_waterfall/index.test.tsx @@ -18,6 +18,13 @@ import { import { setUnifiedDocViewerServices } from '../../../../../plugin'; import type { UnifiedDocViewerServices } from '../../../../../types'; import { mockUnifiedDocViewerServices } from '../../../../../__mocks__'; +import { FlyoutHistoryKeyContext } from '../../../../doc_viewer_flyout/flyout_history_key_context'; + +const testHistoryKey = Symbol('testHistoryKey'); +const renderWithHistoryKey = (ui: React.ReactElement) => + render( + {ui} + ); jest.mock('./waterfall_flyout/span_flyout', () => ({ spanFlyoutId: 'spanDetailFlyout', @@ -106,14 +113,14 @@ describe('FullScreenWaterfall', () => { }); it('should not display nested flyouts initially', () => { - render(); + renderWithHistoryKey(); expect(screen.queryByTestId('spanFlyout')).not.toBeInTheDocument(); expect(screen.queryByTestId('logsFlyout')).not.toBeInTheDocument(); }); it('delays rendering the full trace waterfall on standard open to preserve the flyout animation', () => { - render(); + renderWithHistoryKey(); expect(screen.queryByTestId('fullTraceWaterfall')).not.toBeInTheDocument(); @@ -126,7 +133,7 @@ describe('FullScreenWaterfall', () => { describe('when service name is undefined', () => { it('renders the full trace waterfall after the delay', () => { - render(); + renderWithHistoryKey(); act(() => { jest.advanceTimersByTime(FULL_TRACE_WATERFALL_RENDER_DELAY_MS); @@ -138,13 +145,13 @@ describe('FullScreenWaterfall', () => { describe('animation suppression', () => { it('renders the full trace waterfall immediately when restoring previously-open state', () => { - render(); + renderWithHistoryKey(); expect(screen.getByTestId('fullTraceWaterfall')).toBeInTheDocument(); }); it('injects a style scoped to traceWaterfallFlyout when skipOpenAnimation is true', () => { - render(); + renderWithHistoryKey(); const style = document.getElementById('flyout-skip-open-animation'); expect(style).toBeInTheDocument(); @@ -153,13 +160,13 @@ describe('FullScreenWaterfall', () => { }); it('does not inject the animation-suppression style when skipOpenAnimation is false', () => { - render(); + renderWithHistoryKey(); expect(document.getElementById('flyout-skip-open-animation')).not.toBeInTheDocument(); }); it('removes the animation-suppression style after the timeout', () => { - render(); + renderWithHistoryKey(); expect(document.getElementById('flyout-skip-open-animation')).toBeInTheDocument(); @@ -171,7 +178,7 @@ describe('FullScreenWaterfall', () => { }); it('passes the nested flyout test subject to the restored document flyout', () => { - render( + renderWithHistoryKey( { describe('highlight state management', () => { it('passes initial highlightedSpanId to FullTraceWaterfall', () => { - render( + renderWithHistoryKey( { }); it('updates highlightedSpanId when a node is clicked', () => { - render(); + renderWithHistoryKey(); act(() => { capturedWaterfallProps.onNodeClick?.('clicked-span'); @@ -212,7 +219,7 @@ describe('FullScreenWaterfall', () => { }); it('clears highlightedSpanId when the document flyout is closed', () => { - render( + renderWithHistoryKey( { }); it('sets highlightedSpanId to docId when onErrorClick fires with multiple errors', () => { - render(); + renderWithHistoryKey(); act(() => { capturedWaterfallProps.onErrorClick?.({ @@ -251,7 +258,7 @@ describe('FullScreenWaterfall', () => { }); it('clears highlightedSpanId when onErrorClick fires with a single error', () => { - render( + renderWithHistoryKey( { + const historyKey = useFlyoutHistoryKey(); const { analytics, discoverShared } = getUnifiedDocViewerServices(); const FullTraceWaterfall = discoverShared.features.registry.getById( 'observability-full-trace-waterfall' @@ -187,6 +189,7 @@ export const FullScreenWaterfall = ({ Date: Wed, 25 Mar 2026 13:24:03 -0700 Subject: [PATCH 3/4] Move cascade-close flyout stability test to Traces in Discover --- .../ui/fixtures/flyout_stability/constants.ts | 16 --- .../ui/fixtures/flyout_stability/index.ts | 11 -- .../kbn_archives/logstash_data_view.json | 15 --- .../ui/fixtures/flyout_stability/setup.ts | 27 ----- .../flyout_emotion_cache.spec.ts | 69 ----------- .../flyout_cascade_close.spec.ts | 110 ++++++++++++++++++ 6 files changed, 110 insertions(+), 138 deletions(-) delete mode 100644 src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/constants.ts delete mode 100644 src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/index.ts delete mode 100644 src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/kbn_archives/logstash_data_view.json delete mode 100644 src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/setup.ts delete mode 100644 src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts create mode 100644 src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/flyout_cascade_close.spec.ts diff --git a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/constants.ts b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/constants.ts deleted file mode 100644 index a8952d007f03c5..00000000000000 --- a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/constants.ts +++ /dev/null @@ -1,16 +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", 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". - */ - -export const LOGSTASH = { - DEFAULT_START_TIME: 'Sep 19, 2015 @ 06:31:44.000', - DEFAULT_END_TIME: 'Sep 23, 2015 @ 18:31:44.000', - DATA_VIEW_NAME: 'logstash-*', - KBN_ARCHIVE: - 'src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/kbn_archives/logstash_data_view.json', -}; diff --git a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/index.ts b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/index.ts deleted file mode 100644 index 48ca368ab9884a..00000000000000 --- a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/index.ts +++ /dev/null @@ -1,11 +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", 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". - */ - -export { LOGSTASH } from './constants'; -export { setupFlyoutStability, teardownFlyoutStability } from './setup'; diff --git a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/kbn_archives/logstash_data_view.json b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/kbn_archives/logstash_data_view.json deleted file mode 100644 index 5bf476490dc1ff..00000000000000 --- a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/kbn_archives/logstash_data_view.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "attributes": { - "name": "logstash-*", - "timeFieldName": "@timestamp", - "title": "logstash-*" - }, - "coreMigrationVersion": "8.8.0", - "created_at": "2025-01-01T00:00:00.000Z", - "id": "logstash-data-view", - "managed": false, - "references": [], - "type": "index-pattern", - "typeMigrationVersion": "7.11.0", - "updated_at": "2025-01-01T00:00:00.000Z" -} diff --git a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/setup.ts b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/setup.ts deleted file mode 100644 index 2b24d296ab8a1b..00000000000000 --- a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/flyout_stability/setup.ts +++ /dev/null @@ -1,27 +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", 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 type { ScoutParallelWorkerFixtures } from '@kbn/scout'; -import { LOGSTASH } from './constants'; - -export async function setupFlyoutStability(scoutSpace: ScoutParallelWorkerFixtures['scoutSpace']) { - await scoutSpace.savedObjects.load(LOGSTASH.KBN_ARCHIVE); - await scoutSpace.uiSettings.setDefaultIndex(LOGSTASH.DATA_VIEW_NAME); - await scoutSpace.uiSettings.setDefaultTime({ - from: LOGSTASH.DEFAULT_START_TIME, - to: LOGSTASH.DEFAULT_END_TIME, - }); -} - -export async function teardownFlyoutStability( - scoutSpace: ScoutParallelWorkerFixtures['scoutSpace'] -) { - await scoutSpace.uiSettings.unset('defaultIndex', 'timepicker:timeDefaults'); - await scoutSpace.savedObjects.cleanStandardList(); -} diff --git a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts deleted file mode 100644 index 535a2e691f60ad..00000000000000 --- a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/flyout_stability/flyout_emotion_cache.spec.ts +++ /dev/null @@ -1,69 +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", 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 { tags, spaceTest } from '@kbn/scout'; -import { expect } from '@kbn/scout/ui'; -import { setupFlyoutStability, teardownFlyoutStability } from '../../fixtures/flyout_stability'; - -spaceTest.describe('Flyout system Emotion cache stability', { tag: tags.stateful.classic }, () => { - spaceTest.beforeAll(async ({ scoutSpace }) => { - await setupFlyoutStability(scoutSpace); - }); - - spaceTest.beforeEach(async ({ browserAuth, pageObjects }) => { - await browserAuth.loginAsViewer(); - await pageObjects.discover.goto(); - }); - - spaceTest.afterAll(async ({ scoutSpace }) => { - await teardownFlyoutStability(scoutSpace); - }); - - spaceTest( - 'opening a flyout after a cascade close does not corrupt the Emotion style cache', - async ({ page, pageObjects }) => { - const consoleErrors: string[] = []; - page.on('console', (msg) => { - if (msg.type() === 'error') { - consoleErrors.push(msg.text()); - } - }); - - await spaceTest.step('open a document flyout', async () => { - await pageObjects.discover.waitUntilSearchingHasFinished(); - await pageObjects.discover.openDocumentDetails({ rowIndex: 0 }); - await expect(pageObjects.overlays.docViewerFlyout).toBeVisible(); - }); - - await spaceTest.step('open a second flyout from a different domain', async () => { - await pageObjects.overlays.openNewsfeedFlyout(); - }); - - await spaceTest.step('close the second flyout, triggering a cascade close', async () => { - await pageObjects.overlays.closeNewsfeedFlyout(); - await expect(pageObjects.overlays.docViewerFlyout).toBeHidden(); - }); - - await spaceTest.step('open a new flyout after the cascade close', async () => { - await pageObjects.discover.openDocumentDetails({ rowIndex: 0 }); - await expect(pageObjects.overlays.docViewerFlyout).toBeVisible(); - }); - - await spaceTest.step( - 'verify no insertBefore errors from stale Emotion cache refs', - async () => { - await expect(page.testSubj.locator('discoverDocumentsTable')).toBeVisible(); - - const hasInsertBeforeError = consoleErrors.some((msg) => msg.includes('insertBefore')); - expect(hasInsertBeforeError).toBe(false); - } - ); - } - ); -}); diff --git a/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/flyout_cascade_close.spec.ts b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/flyout_cascade_close.spec.ts new file mode 100644 index 00000000000000..8906078765c471 --- /dev/null +++ b/src/platform/plugins/shared/discover/test/scout/ui/parallel_tests/traces_experience/flyout_cascade_close.spec.ts @@ -0,0 +1,110 @@ +/* + * 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 { tags } from '@kbn/scout'; +import { expect } from '@kbn/scout/ui'; +import type { PageObjects } from '@kbn/scout'; +import { + spaceTest, + TRACES, + RICH_TRACE, + setupTracesExperience, + teardownTracesExperience, +} from '../../fixtures/traces_experience'; +import type { TracesFlyout } from '../../fixtures/traces_experience/page_objects/flyout'; + +type DiscoverPage = PageObjects['discover']; + +const openDocViewerAndTraceTimeline = async (pageObjects: { + discover: DiscoverPage; + tracesExperience: { + openOverviewTab: (d: DiscoverPage) => Promise; + flyout: TracesFlyout; + }; +}) => { + await pageObjects.discover.writeAndSubmitEsqlQuery( + `${TRACES.ESQL_QUERY} | WHERE transaction.name == "${RICH_TRACE.TRANSACTION_NAME}"` + ); + await pageObjects.tracesExperience.openOverviewTab(pageObjects.discover); + await pageObjects.tracesExperience.flyout.traceSummary.fullScreenButton.click(); + await expect(pageObjects.tracesExperience.flyout.waterfallFlyout.container).toBeVisible(); +}; + +spaceTest.describe( + 'Traces in Discover - Flyout cascade-close Emotion cache stability', + { + tag: [...tags.stateful.all, ...tags.serverless.observability.complete], + }, + () => { + spaceTest.beforeAll(async ({ scoutSpace, config }) => { + await setupTracesExperience(scoutSpace, config); + }); + + spaceTest.beforeEach(async ({ browserAuth, pageObjects, page }) => { + await browserAuth.loginAsViewer(); + await pageObjects.discover.goto(); + // Suppress the tour so it does not overlap the waterfall flyout interactions. + await page.evaluate(() => localStorage.setItem('fullscreenWaterfallTourDismissed', 'true')); + }); + + spaceTest.afterAll(async ({ scoutSpace }) => { + await teardownTracesExperience(scoutSpace); + }); + + spaceTest( + 'closing the Trace Timeline flyout navigates back to the Document Viewer without corrupting the Emotion style cache', + async ({ page, pageObjects }) => { + const { flyout } = pageObjects.tracesExperience; + const docViewerFlyout = page.testSubj.locator('docViewerFlyout'); + + const consoleErrors: string[] = []; + page.on('console', (msg) => { + if (msg.type() === 'error') { + consoleErrors.push(msg.text()); + } + }); + + await spaceTest.step( + 'open the Document Viewer and Trace Timeline flyouts', + async () => { + await pageObjects.discover.waitUntilSearchingHasFinished(); + await openDocViewerAndTraceTimeline(pageObjects); + } + ); + + await spaceTest.step( + 'close the Trace Timeline flyout via the Back button', + async () => { + await flyout.waterfallFlyout.backButton.click(); + await expect(flyout.waterfallFlyout.container).toBeHidden(); + // The Document Viewer remains visible — it was not cascade-closed because + // both flyouts share the same EUI flyout-manager historyKey. + await expect(docViewerFlyout).toBeVisible(); + } + ); + + await spaceTest.step( + 're-open the Trace Timeline flyout after navigating back', + async () => { + await flyout.traceSummary.fullScreenButton.click(); + await expect(flyout.waterfallFlyout.container).toBeVisible(); + } + ); + + await spaceTest.step( + 'verify no insertBefore errors from stale Emotion cache refs', + async () => { + const hasInsertBeforeError = consoleErrors.some((msg) => msg.includes('insertBefore')); + expect(hasInsertBeforeError).toBe(false); + } + ); + } + ); + } +); From 04f5bc9c7766e2dca3e01fb26338171595a6a662 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 25 Mar 2026 14:02:38 -0700 Subject: [PATCH 4/4] =?UTF-8?q?Fix=20for=20'Explore=20from=20APM=20?= =?UTF-8?q?=E2=80=BA=20Errors=20page=20-=20"Open=20in=20Discover"'=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scout/ui/fixtures/traces_experience/page_objects/apm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/apm.ts b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/apm.ts index d6d0362abd3d3e..bb801561fc4dff 100644 --- a/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/apm.ts +++ b/src/platform/plugins/shared/discover/test/scout/ui/fixtures/traces_experience/page_objects/apm.ts @@ -43,7 +43,7 @@ export function createApmPage(page: ScoutPage): ApmPage { ? page.testSubj.locator(tableTestSubj) : page.locator('table'); - await container.getByText(linkText).click(); + await container.getByText(linkText).first().click(); }, async clickWaterfallItem(itemName: string) {