diff --git a/packages/playwright-core/src/server/injected/injectedScript.ts b/packages/playwright-core/src/server/injected/injectedScript.ts
index 6bd763d43e..6013f35567 100644
--- a/packages/playwright-core/src/server/injected/injectedScript.ts
+++ b/packages/playwright-core/src/server/injected/injectedScript.ts
@@ -1133,7 +1133,7 @@ export class InjectedScript {
bubbles: true,
cancelable: true,
detail: callId,
- composed: false,
+ composed: true,
});
for (const element of markedElements)
element.dispatchEvent(customEvent);
diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts
index 2db04b1278..2340f55a26 100644
--- a/packages/playwright-core/src/server/trace/recorder/snapshotter.ts
+++ b/packages/playwright-core/src/server/trace/recorder/snapshotter.ts
@@ -114,7 +114,7 @@ export class Snapshotter {
bubbles: true,
cancelable: true,
detail: callId,
- composed: false,
+ composed: true,
});
element.dispatchEvent(customEvent);
}, callId);
diff --git a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
index ae8e3de5d5..ee503eb043 100644
--- a/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
+++ b/packages/playwright-core/src/server/trace/recorder/snapshotterInjected.ts
@@ -136,7 +136,7 @@ export function frameSnapshotStreamer(snapshotStreamer: string) {
if (!event.detail)
return;
const callId = event.detail as string;
- (event.target as any).__playwright_target__ = callId;
+ (event.composedPath()[0] as any).__playwright_target__ = callId;
});
}
diff --git a/packages/trace-viewer/src/snapshotRenderer.ts b/packages/trace-viewer/src/snapshotRenderer.ts
index 0dc67da521..dcea1d9670 100644
--- a/packages/trace-viewer/src/snapshotRenderer.ts
+++ b/packages/trace-viewer/src/snapshotRenderer.ts
@@ -104,9 +104,7 @@ export class SnapshotRenderer {
const prefix = snapshot.doctype ? `` : '';
html = prefix + [
'',
- ``,
- ``,
- ``
+ ``
].join('') + html;
return { html, pageId: snapshot.pageId, frameId: snapshot.frameId, index: this._index };
@@ -195,8 +193,8 @@ function snapshotNodes(snapshot: FrameSnapshot): NodeSnapshot[] {
return (snapshot as any)._nodes;
}
-function snapshotScript() {
- function applyPlaywrightAttributes(unwrapPopoutUrl: (url: string) => string) {
+function snapshotScript(...targetIds: (string | undefined)[]) {
+ function applyPlaywrightAttributes(unwrapPopoutUrl: (url: string) => string, ...targetIds: (string | undefined)[]) {
const scrollTops: Element[] = [];
const scrollLefts: Element[] = [];
@@ -220,6 +218,14 @@ function snapshotScript() {
element.removeAttribute('__playwright_selected_');
}
+ for (const targetId of targetIds) {
+ for (const target of root.querySelectorAll(`[__playwright_target__="${targetId}"]`)) {
+ const style = (target as HTMLElement).style;
+ style.outline = '2px solid #006ab1';
+ style.backgroundColor = '#6fa8dc7f';
+ }
+ }
+
for (const iframe of root.querySelectorAll('iframe, frame')) {
const src = iframe.getAttribute('__playwright_src__');
if (!src) {
@@ -303,7 +309,7 @@ function snapshotScript() {
window.addEventListener('DOMContentLoaded', onDOMContentLoaded);
}
- return `\n(${applyPlaywrightAttributes.toString()})(${unwrapPopoutUrl.toString()})`;
+ return `\n(${applyPlaywrightAttributes.toString()})(${unwrapPopoutUrl.toString()}${targetIds.map(id => `, "${id}"`).join('')})`;
}
diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts
index 4f37c2d930..3614505a74 100644
--- a/tests/library/trace-viewer.spec.ts
+++ b/tests/library/trace-viewer.spec.ts
@@ -630,6 +630,20 @@ test('should highlight target elements', async ({ page, runAndTrace, browserName
await expect.poll(() => highlightedDivs(frameExpect2)).toEqual(['multi', 'multi']);
});
+test('should highlight target element in shadow dom', async ({ page, server, runAndTrace }) => {
+ const traceViewer = await runAndTrace(async () => {
+ await page.goto(server.PREFIX + '/shadow.html');
+ await page.locator('button').click();
+ await expect(page.locator('h1')).toHaveText('Hellow Shadow DOM v1');
+ });
+
+ const framePageClick = await traceViewer.snapshotFrame('locator.click');
+ await expect(framePageClick.locator('button')).toHaveCSS('background-color', 'rgba(111, 168, 220, 0.498)');
+
+ const frameExpect = await traceViewer.snapshotFrame('expect.toHaveText');
+ await expect(frameExpect.locator('h1')).toHaveCSS('background-color', 'rgba(111, 168, 220, 0.498)');
+});
+
test('should show action source', async ({ showTraceViewer }) => {
const traceViewer = await showTraceViewer([traceFile]);
await traceViewer.selectAction('locator.click');