diff --git a/packages/trace-viewer/src/sw/snapshotRenderer.ts b/packages/trace-viewer/src/sw/snapshotRenderer.ts index 93363878ab..df340af2e4 100644 --- a/packages/trace-viewer/src/sw/snapshotRenderer.ts +++ b/packages/trace-viewer/src/sw/snapshotRenderer.ts @@ -238,7 +238,9 @@ function snapshotNodes(snapshot: FrameSnapshot): NodeSnapshot[] { function snapshotScript(...targetIds: (string | undefined)[]) { function applyPlaywrightAttributes(unwrapPopoutUrl: (url: string) => string, ...targetIds: (string | undefined)[]) { - const isUnderTest = new URLSearchParams(location.search).has('isUnderTest'); + const searchParams = new URLSearchParams(location.search); + const shouldPopulateCanvasFromScreenshot = searchParams.has('shouldPopulateCanvasFromScreenshot'); + const isUnderTest = searchParams.has('isUnderTest'); const kPointerWarningTitle = 'Recorded click position in absolute coordinates did not' + ' match the center of the clicked element. This is likely due to a difference between' + @@ -455,15 +457,20 @@ function snapshotScript(...targetIds: (string | undefined)[]) { drawCheckerboard(context, canvas); - context.drawImage(img, boundingRect.left * img.width, boundingRect.top * img.height, (boundingRect.right - boundingRect.left) * img.width, (boundingRect.bottom - boundingRect.top) * img.height, 0, 0, canvas.width, canvas.height); + if (shouldPopulateCanvasFromScreenshot) { + context.drawImage(img, boundingRect.left * img.width, boundingRect.top * img.height, (boundingRect.right - boundingRect.left) * img.width, (boundingRect.bottom - boundingRect.top) * img.height, 0, 0, canvas.width, canvas.height); + + if (partiallyUncaptured) + canvas.title = `Playwright couldn't capture full canvas contents because it's located partially outside the viewport.`; + else + canvas.title = `Canvas contents are displayed on a best-effort basis based on viewport screenshots taken during test execution.`; + } else { + canvas.title = 'Canvas content display is disabled.'; + } + if (isUnderTest) // eslint-disable-next-line no-console console.log(`canvas drawn:`, JSON.stringify([boundingRect.left, boundingRect.top, (boundingRect.right - boundingRect.left), (boundingRect.bottom - boundingRect.top)].map(v => Math.floor(v * 100)))); - - if (partiallyUncaptured) - canvas.title = `Playwright couldn't capture full canvas contents because it's located partially outside the viewport.`; - else - canvas.title = `Canvas contents are displayed on a best-effort basis based on viewport screenshots taken during test execution.`; } }; img.onerror = () => { diff --git a/packages/trace-viewer/src/ui/defaultSettingsView.tsx b/packages/trace-viewer/src/ui/defaultSettingsView.tsx index c36ddd1458..5b55268af9 100644 --- a/packages/trace-viewer/src/ui/defaultSettingsView.tsx +++ b/packages/trace-viewer/src/ui/defaultSettingsView.tsx @@ -17,13 +17,25 @@ import * as React from 'react'; import { SettingsView } from './settingsView'; import { useDarkModeSetting } from '@web/theme'; +import { useShouldPopulateCanvasFromScreenshot } from './settings/useShouldPopulateCanvasFromScreenshot'; export const DefaultSettingsView: React.FC<{}> = () => { + const [ + shouldPopulateCanvasFromScreenshot, + setShouldPopulateCanvasFromScreenshot, + ] = useShouldPopulateCanvasFromScreenshot(); const [darkMode, setDarkMode] = useDarkModeSetting(); return ( ); }; diff --git a/packages/trace-viewer/src/ui/settings/useShouldPopulateCanvasFromScreenshot.ts b/packages/trace-viewer/src/ui/settings/useShouldPopulateCanvasFromScreenshot.ts new file mode 100644 index 0000000000..163dd50ec0 --- /dev/null +++ b/packages/trace-viewer/src/ui/settings/useShouldPopulateCanvasFromScreenshot.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useSetting } from '@web/uiUtils'; + +export const useShouldPopulateCanvasFromScreenshot = (): [ + boolean, + (value: boolean) => void +] => useSetting('shouldPopulateCanvasFromScreenshot', false); diff --git a/packages/trace-viewer/src/ui/snapshotTab.tsx b/packages/trace-viewer/src/ui/snapshotTab.tsx index 8383890a2e..70eec996fb 100644 --- a/packages/trace-viewer/src/ui/snapshotTab.tsx +++ b/packages/trace-viewer/src/ui/snapshotTab.tsx @@ -30,6 +30,7 @@ import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser'; import { TabbedPaneTab } from '@web/components/tabbedPane'; import { BrowserFrame } from './browserFrame'; import type { ElementInfo } from '@recorder/recorderTypes'; +import { useShouldPopulateCanvasFromScreenshot } from './settings/useShouldPopulateCanvasFromScreenshot'; export const SnapshotTabsView: React.FunctionComponent<{ action: ActionTraceEvent | undefined, @@ -43,13 +44,16 @@ export const SnapshotTabsView: React.FunctionComponent<{ }> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator }) => { const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action'); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [shouldPopulateCanvasFromScreenshot, _] = useShouldPopulateCanvasFromScreenshot(); + const snapshots = React.useMemo(() => { return collectSnapshots(action); }, [action]); const snapshotUrls = React.useMemo(() => { const snapshot = snapshots[snapshotTab]; - return snapshot ? extendSnapshot(snapshot) : undefined; - }, [snapshots, snapshotTab]); + return snapshot ? extendSnapshot(snapshot, shouldPopulateCanvasFromScreenshot) : undefined; + }, [snapshots, snapshotTab, shouldPopulateCanvasFromScreenshot]); return
@@ -327,7 +331,7 @@ export function collectSnapshots(action: ActionTraceEvent | undefined): Snapshot const isUnderTest = new URLSearchParams(window.location.search).has('isUnderTest'); const serverParam = new URLSearchParams(window.location.search).get('server'); -export function extendSnapshot(snapshot: Snapshot): SnapshotUrls { +export function extendSnapshot(snapshot: Snapshot, shouldPopulateCanvasFromScreenshot: boolean): SnapshotUrls { const params = new URLSearchParams(); params.set('trace', context(snapshot.action).traceUrl); params.set('name', snapshot.snapshotName); @@ -339,6 +343,9 @@ export function extendSnapshot(snapshot: Snapshot): SnapshotUrls { if (snapshot.hasInputTarget) params.set('hasInputTarget', '1'); } + if (shouldPopulateCanvasFromScreenshot) + params.set('shouldPopulateCanvasFromScreenshot', '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();