diff --git a/packages/trace-viewer/src/snapshotRenderer.ts b/packages/trace-viewer/src/snapshotRenderer.ts
index dcea1d9670..e156dcbaf9 100644
--- a/packages/trace-viewer/src/snapshotRenderer.ts
+++ b/packages/trace-viewer/src/snapshotRenderer.ts
@@ -197,6 +197,7 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
function applyPlaywrightAttributes(unwrapPopoutUrl: (url: string) => string, ...targetIds: (string | undefined)[]) {
const scrollTops: Element[] = [];
const scrollLefts: Element[] = [];
+ const targetElements: Element[] = [];
const visit = (root: Document | ShadowRoot) => {
// Collect all scrolled elements for later use.
@@ -223,6 +224,7 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
const style = (target as HTMLElement).style;
style.outline = '2px solid #006ab1';
style.backgroundColor = '#6fa8dc7f';
+ targetElements.push(target);
}
}
@@ -231,10 +233,8 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
if (!src) {
iframe.setAttribute('src', 'data:text/html,
');
} else {
- // Append query parameters to inherit ?name= or ?time= values from parent.
+ // Retain query parameters to inherit name=, time=, showPoint= and other values from parent.
const url = new URL(unwrapPopoutUrl(window.location.href));
- url.searchParams.delete('pointX');
- url.searchParams.delete('pointY');
// We can be loading iframe from within iframe, reset base to be absolute.
const index = url.pathname.lastIndexOf('/snapshot/');
if (index !== -1)
@@ -284,23 +284,25 @@ function snapshotScript(...targetIds: (string | undefined)[]) {
element.removeAttribute('__playwright_scroll_left_');
}
- const search = new URL(window.location.href).searchParams;
- const pointX = search.get('pointX');
- const pointY = search.get('pointY');
- if (pointX) {
- const pointElement = document.createElement('x-pw-pointer');
- pointElement.style.position = 'fixed';
- pointElement.style.backgroundColor = '#f44336';
- pointElement.style.width = '20px';
- pointElement.style.height = '20px';
- pointElement.style.borderRadius = '10px';
- pointElement.style.margin = '-10px 0 0 -10px';
- pointElement.style.zIndex = '2147483647';
- pointElement.style.left = pointX + 'px';
- pointElement.style.top = pointY + 'px';
- document.documentElement.appendChild(pointElement);
- }
document.styleSheets[0].disabled = true;
+
+ const search = new URL(window.location.href).searchParams;
+ if (search.get('showPoint')) {
+ for (const target of targetElements) {
+ const pointElement = document.createElement('x-pw-pointer');
+ pointElement.style.position = 'fixed';
+ pointElement.style.backgroundColor = '#f44336';
+ pointElement.style.width = '20px';
+ pointElement.style.height = '20px';
+ pointElement.style.borderRadius = '10px';
+ pointElement.style.margin = '-10px 0 0 -10px';
+ pointElement.style.zIndex = '2147483647';
+ const box = target.getBoundingClientRect();
+ pointElement.style.left = (box.left + box.width / 2) + 'px';
+ pointElement.style.top = (box.top + box.height / 2) + 'px';
+ document.documentElement.appendChild(pointElement);
+ }
+ }
};
const onDOMContentLoaded = () => visit(document);
diff --git a/packages/trace-viewer/src/ui/snapshotTab.tsx b/packages/trace-viewer/src/ui/snapshotTab.tsx
index b6efbd211a..c3d4919336 100644
--- a/packages/trace-viewer/src/ui/snapshotTab.tsx
+++ b/packages/trace-viewer/src/ui/snapshotTab.tsx
@@ -42,23 +42,24 @@ export const SnapshotTab: React.FunctionComponent<{
const [measure, ref] = useMeasure();
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
+ type Snapshot = { action: ActionTraceEvent, snapshotName: string, showPoint?: boolean };
const { snapshots } = React.useMemo(() => {
if (!action)
return { snapshots: {} };
// if the action has no beforeSnapshot, use the last available afterSnapshot.
- let beforeSnapshot = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined;
+ let beforeSnapshot: Snapshot | undefined = action.beforeSnapshot ? { action, snapshotName: action.beforeSnapshot } : undefined;
let a = action;
while (!beforeSnapshot && a) {
a = prevInList(a);
beforeSnapshot = a?.afterSnapshot ? { action: a, snapshotName: a?.afterSnapshot } : undefined;
}
- const afterSnapshot = action.afterSnapshot ? { action, snapshotName: action.afterSnapshot } : beforeSnapshot;
- const actionSnapshot = action.inputSnapshot ? { action, snapshotName: action.inputSnapshot } : afterSnapshot;
+ const afterSnapshot: Snapshot | undefined = action.afterSnapshot ? { action, snapshotName: action.afterSnapshot } : beforeSnapshot;
+ const actionSnapshot: Snapshot | undefined = action.inputSnapshot ? { action, snapshotName: action.inputSnapshot, showPoint: !!action.point } : afterSnapshot;
return { snapshots: { action: actionSnapshot, before: beforeSnapshot, after: afterSnapshot } };
}, [action]);
- const { snapshotInfoUrl, snapshotUrl, pointX, pointY, popoutUrl } = React.useMemo(() => {
+ const { snapshotInfoUrl, snapshotUrl, popoutUrl } = React.useMemo(() => {
const snapshot = snapshots[snapshotTab];
if (!snapshot)
return { snapshotUrl: kBlankSnapshotUrl };
@@ -66,16 +67,18 @@ export const SnapshotTab: React.FunctionComponent<{
const params = new URLSearchParams();
params.set('trace', context(snapshot.action).traceUrl);
params.set('name', snapshot.snapshotName);
+ if (snapshot.showPoint)
+ params.set('showPoint', '1');
const snapshotUrl = new URL(`snapshot/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString();
const snapshotInfoUrl = new URL(`snapshotInfo/${snapshot.action.pageId}?${params.toString()}`, window.location.href).toString();
- const pointX = snapshotTab === 'action' ? snapshot.action.point?.x : undefined;
- const pointY = snapshotTab === 'action' ? snapshot.action.point?.y : undefined;
const popoutParams = new URLSearchParams();
popoutParams.set('r', snapshotUrl);
popoutParams.set('trace', context(snapshot.action).traceUrl);
+ if (snapshot.showPoint)
+ popoutParams.set('showPoint', '1');
const popoutUrl = new URL(`snapshot.html?${popoutParams.toString()}`, window.location.href).toString();
- return { snapshots, snapshotInfoUrl, snapshotUrl, pointX, pointY, popoutUrl };
+ return { snapshots, snapshotInfoUrl, snapshotUrl, popoutUrl };
}, [snapshots, snapshotTab]);
const iframeRef0 = React.useRef(null);
@@ -111,12 +114,11 @@ export const SnapshotTab: React.FunctionComponent<{
iframe.addEventListener('load', loadedCallback);
iframe.addEventListener('error', loadedCallback);
- const newUrl = snapshotUrl + (pointX === undefined ? '' : `&pointX=${pointX}&pointY=${pointY}`);
// Try preventing history entry from being created.
if (iframe.contentWindow)
- iframe.contentWindow.location.replace(newUrl);
+ iframe.contentWindow.location.replace(snapshotUrl);
else
- iframe.src = newUrl;
+ iframe.src = snapshotUrl;
await loadedPromise;
} catch {
@@ -132,7 +134,7 @@ export const SnapshotTab: React.FunctionComponent<{
loadingRef.current.visibleIframe = newVisibleIframe;
setSnapshotInfo(newSnapshotInfo);
})();
- }, [snapshotUrl, snapshotInfoUrl, pointX, pointY]);
+ }, [snapshotUrl, snapshotInfoUrl]);
const windowHeaderHeight = 40;
const snapshotContainerSize = {