diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
index 0400c6deaf..4d575ef0e9 100644
--- a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
+++ b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
@@ -48,6 +48,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
const kCustomElementsAttribute = '__playwright_custom_elements__';
const kCurrentSrcAttribute = '__playwright_current_src__';
const kBoundingRectAttribute = '__playwright_bounding_rect__';
+ const kPopoverOpenAttribute = '__playwright_popover_open_';
// Symbols for our own info on Nodes/StyleSheets.
const kSnapshotFrameId = Symbol('__playwright_snapshot_frameid_');
@@ -449,6 +450,12 @@ export function frameSnapshotStreamer(snapshotStreamer: string, removeNoScript:
expectValue(value);
attrs[kBoundingRectAttribute] = value;
}
+ if ((element as HTMLElement).popover && (element as HTMLElement).matches && (element as HTMLElement).matches(':popover-open')) {
+ const value = 'true';
+ expectValue(kPopoverOpenAttribute);
+ expectValue(value);
+ attrs[kPopoverOpenAttribute] = value;
+ }
if (element.scrollTop) {
expectValue(kScrollTopAttribute);
expectValue(element.scrollTop);
diff --git a/packages/trace-viewer/src/sw/snapshotRenderer.ts b/packages/trace-viewer/src/sw/snapshotRenderer.ts
index f4e9d908fb..93363878ab 100644
--- a/packages/trace-viewer/src/sw/snapshotRenderer.ts
+++ b/packages/trace-viewer/src/sw/snapshotRenderer.ts
@@ -270,6 +270,13 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
(element as HTMLOptionElement).selected = element.getAttribute('__playwright_selected_') === 'true';
element.removeAttribute('__playwright_selected_');
}
+ for (const element of root.querySelectorAll(`[__playwright_popover_open_]`)) {
+ try {
+ (element as HTMLElement).showPopover();
+ } catch {
+ }
+ element.removeAttribute('__playwright_popover_open_');
+ }
for (const targetId of targetIds) {
for (const target of root.querySelectorAll(`[__playwright_target__="${targetId}"]`)) {
diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts
index 7e64c62156..2bd7b85888 100644
--- a/tests/library/trace-viewer.spec.ts
+++ b/tests/library/trace-viewer.spec.ts
@@ -1572,3 +1572,20 @@ test('should show only one pointer with multilevel iframes', async ({ page, runA
await expect.soft(snapshotFrame.frameLocator('iframe').locator('x-pw-pointer')).not.toBeAttached();
await expect.soft(snapshotFrame.frameLocator('iframe').frameLocator('iframe').locator('x-pw-pointer')).toBeVisible();
});
+
+test('should show a popover', async ({ runAndTrace, page, server }) => {
+ const traceViewer = await runAndTrace(async () => {
+ await page.setContent(`
+
+
+ I'm a popover
+
+ `);
+ await page.getByRole('button').click();
+ await expect(page.locator('div')).toBeVisible();
+ });
+
+ const snapshot = await traceViewer.snapshotFrame('expect.toBeVisible');
+ const popover = snapshot.locator('#pop');
+ await expect.poll(() => popover.evaluate(e => e.matches(':popover-open'))).toBe(true);
+});