mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(trace): remove screenshot instead of snapshot code (#33225)
This commit is contained in:
parent
6bfdad068c
commit
3322a7f3bb
@ -91,7 +91,7 @@ export const EmbeddedWorkbenchLoader: React.FunctionComponent = () => {
|
|||||||
<div className='progress'>
|
<div className='progress'>
|
||||||
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
||||||
</div>
|
</div>
|
||||||
<Workbench model={model} openPage={openPage} onOpenExternally={openSourceLocation} showSettings />
|
<Workbench model={model} openPage={openPage} onOpenExternally={openSourceLocation} />
|
||||||
{!traceURLs.length && <div className='empty-state'>
|
{!traceURLs.length && <div className='empty-state'>
|
||||||
<div className='title'>Select test to see the trace</div>
|
<div className='title'>Select test to see the trace</div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|||||||
@ -22,16 +22,15 @@ import * as React from 'react';
|
|||||||
import './sourceTab.css';
|
import './sourceTab.css';
|
||||||
|
|
||||||
export const InspectorTab: React.FunctionComponent<{
|
export const InspectorTab: React.FunctionComponent<{
|
||||||
showScreenshot: boolean,
|
|
||||||
sdkLanguage: Language,
|
sdkLanguage: Language,
|
||||||
setIsInspecting: (isInspecting: boolean) => void,
|
setIsInspecting: (isInspecting: boolean) => void,
|
||||||
highlightedLocator: string,
|
highlightedLocator: string,
|
||||||
setHighlightedLocator: (locator: string) => void,
|
setHighlightedLocator: (locator: string) => void,
|
||||||
}> = ({ showScreenshot, sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => {
|
}> = ({ sdkLanguage, setIsInspecting, highlightedLocator, setHighlightedLocator }) => {
|
||||||
return <div className='vbox' style={{ backgroundColor: 'var(--vscode-sideBar-background)' }}>
|
return <div className='vbox' style={{ backgroundColor: 'var(--vscode-sideBar-background)' }}>
|
||||||
<div style={{ margin: '10px 0px 10px 10px', color: 'var(--vscode-editorCodeLens-foreground)', flex: 'none' }}>Locator</div>
|
<div style={{ margin: '10px 0px 10px 10px', color: 'var(--vscode-editorCodeLens-foreground)', flex: 'none' }}>Locator</div>
|
||||||
<div style={{ margin: '0 10px 10px', flex: 'auto' }}>
|
<div style={{ margin: '0 10px 10px', flex: 'auto' }}>
|
||||||
<CodeMirrorWrapper text={showScreenshot ? '/* disable "show screenshot" setting to pick locator */' : highlightedLocator} language={sdkLanguage} focusOnChange={true} isFocused={true} wrapLines={true} onChange={text => {
|
<CodeMirrorWrapper text={highlightedLocator} language={sdkLanguage} focusOnChange={true} isFocused={true} wrapLines={true} onChange={text => {
|
||||||
// Updating text needs to go first - react can squeeze a render between the state updates.
|
// Updating text needs to go first - react can squeeze a render between the state updates.
|
||||||
setHighlightedLocator(text);
|
setHighlightedLocator(text);
|
||||||
setIsInspecting(false);
|
setIsInspecting(false);
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import type { ActionEntry, ContextEntry, PageEntry } from '../types/entries';
|
|||||||
import type { StackFrame } from '@protocol/channels';
|
import type { StackFrame } from '@protocol/channels';
|
||||||
|
|
||||||
const contextSymbol = Symbol('context');
|
const contextSymbol = Symbol('context');
|
||||||
const pageSymbol = Symbol('page');
|
|
||||||
const nextInContextSymbol = Symbol('next');
|
const nextInContextSymbol = Symbol('next');
|
||||||
const prevInListSymbol = Symbol('prev');
|
const prevInListSymbol = Symbol('prev');
|
||||||
const eventsSymbol = Symbol('events');
|
const eventsSymbol = Symbol('events');
|
||||||
@ -148,7 +147,6 @@ function indexModel(context: ContextEntry) {
|
|||||||
for (let i = 0; i < context.actions.length; ++i) {
|
for (let i = 0; i < context.actions.length; ++i) {
|
||||||
const action = context.actions[i] as any;
|
const action = context.actions[i] as any;
|
||||||
action[contextSymbol] = context;
|
action[contextSymbol] = context;
|
||||||
action[pageSymbol] = context.pages.find(page => page.pageId === action.pageId);
|
|
||||||
}
|
}
|
||||||
let lastNonRouteAction = undefined;
|
let lastNonRouteAction = undefined;
|
||||||
for (let i = context.actions.length - 1; i >= 0; i--) {
|
for (let i = context.actions.length - 1; i >= 0; i--) {
|
||||||
@ -353,10 +351,6 @@ export function prevInList(action: ActionTraceEvent): ActionTraceEvent {
|
|||||||
return (action as any)[prevInListSymbol];
|
return (action as any)[prevInListSymbol];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pageForAction(action: ActionTraceEvent): PageEntry {
|
|
||||||
return (action as any)[pageSymbol];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stats(action: ActionTraceEvent): { errors: number, warnings: number } {
|
export function stats(action: ActionTraceEvent): { errors: number, warnings: number } {
|
||||||
let errors = 0;
|
let errors = 0;
|
||||||
let warnings = 0;
|
let warnings = 0;
|
||||||
|
|||||||
@ -213,7 +213,6 @@ const PropertiesView: React.FunctionComponent<{
|
|||||||
id: 'inspector',
|
id: 'inspector',
|
||||||
title: 'Locator',
|
title: 'Locator',
|
||||||
render: () => <InspectorTab
|
render: () => <InspectorTab
|
||||||
showScreenshot={false}
|
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocator}
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
import './snapshotTab.css';
|
import './snapshotTab.css';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import type { ActionTraceEvent } from '@trace/trace';
|
import type { ActionTraceEvent } from '@trace/trace';
|
||||||
import { context, type MultiTraceModel, pageForAction, prevInList } from './modelUtil';
|
import { context, type MultiTraceModel, prevInList } from './modelUtil';
|
||||||
import { Toolbar } from '@web/components/toolbar';
|
import { Toolbar } from '@web/components/toolbar';
|
||||||
import { ToolbarButton } from '@web/components/toolbarButton';
|
import { ToolbarButton } from '@web/components/toolbarButton';
|
||||||
import { clsx, useMeasure } from '@web/uiUtils';
|
import { clsx, useMeasure } from '@web/uiUtils';
|
||||||
@ -29,18 +29,8 @@ import type { Language } from '@isomorphic/locatorGenerators';
|
|||||||
import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser';
|
import { locatorOrSelectorAsSelector } from '@isomorphic/locatorParser';
|
||||||
import { TabbedPaneTab } from '@web/components/tabbedPane';
|
import { TabbedPaneTab } from '@web/components/tabbedPane';
|
||||||
import { BrowserFrame } from './browserFrame';
|
import { BrowserFrame } from './browserFrame';
|
||||||
import { ClickPointer } from './clickPointer';
|
|
||||||
import type { ElementInfo } from '@recorder/recorderTypes';
|
import type { ElementInfo } from '@recorder/recorderTypes';
|
||||||
|
|
||||||
function findClosest<T>(items: T[], metric: (v: T) => number, target: number) {
|
|
||||||
return items.find((item, index) => {
|
|
||||||
if (index === items.length - 1)
|
|
||||||
return true;
|
|
||||||
const next = items[index + 1];
|
|
||||||
return Math.abs(metric(item) - target) < Math.abs(metric(next) - target);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SnapshotTabsView: React.FunctionComponent<{
|
export const SnapshotTabsView: React.FunctionComponent<{
|
||||||
action: ActionTraceEvent | undefined,
|
action: ActionTraceEvent | undefined,
|
||||||
model?: MultiTraceModel,
|
model?: MultiTraceModel,
|
||||||
@ -53,7 +43,6 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
|||||||
openPage?: (url: string, target?: string) => Window | any,
|
openPage?: (url: string, target?: string) => Window | any,
|
||||||
}> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => {
|
}> = ({ action, sdkLanguage, testIdAttributeName, isInspecting, setIsInspecting, highlightedLocator, setHighlightedLocator, openPage }) => {
|
||||||
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
|
const [snapshotTab, setSnapshotTab] = React.useState<'action'|'before'|'after'>('action');
|
||||||
const showScreenshotInsteadOfSnapshot = false;
|
|
||||||
|
|
||||||
const snapshots = React.useMemo(() => {
|
const snapshots = React.useMemo(() => {
|
||||||
return collectSnapshots(action);
|
return collectSnapshots(action);
|
||||||
@ -65,7 +54,7 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
|||||||
|
|
||||||
return <div className='snapshot-tab vbox'>
|
return <div className='snapshot-tab vbox'>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<ToolbarButton className='pick-locator' title={showScreenshotInsteadOfSnapshot ? 'Disable "screenshots instead of snapshots" to pick a locator' : 'Pick locator'} icon='target' toggled={isInspecting} onClick={() => setIsInspecting(!isInspecting)} disabled={showScreenshotInsteadOfSnapshot} />
|
<ToolbarButton className='pick-locator' title='Pick locator' icon='target' toggled={isInspecting} onClick={() => setIsInspecting(!isInspecting)} />
|
||||||
{['action', 'before', 'after'].map(tab => {
|
{['action', 'before', 'after'].map(tab => {
|
||||||
return <TabbedPaneTab
|
return <TabbedPaneTab
|
||||||
key={tab}
|
key={tab}
|
||||||
@ -76,7 +65,7 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
|||||||
></TabbedPaneTab>;
|
></TabbedPaneTab>;
|
||||||
})}
|
})}
|
||||||
<div style={{ flex: 'auto' }}></div>
|
<div style={{ flex: 'auto' }}></div>
|
||||||
<ToolbarButton icon='link-external' title={showScreenshotInsteadOfSnapshot ? 'Not available when showing screenshot' : 'Open snapshot in a new tab'} disabled={!snapshotUrls?.popoutUrl || showScreenshotInsteadOfSnapshot} onClick={() => {
|
<ToolbarButton icon='link-external' title='Open snapshot in a new tab' disabled={!snapshotUrls?.popoutUrl} onClick={() => {
|
||||||
if (!openPage)
|
if (!openPage)
|
||||||
openPage = window.open;
|
openPage = window.open;
|
||||||
const win = openPage(snapshotUrls?.popoutUrl || '', '_blank');
|
const win = openPage(snapshotUrls?.popoutUrl || '', '_blank');
|
||||||
@ -86,7 +75,7 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
|||||||
});
|
});
|
||||||
}} />
|
}} />
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
{!showScreenshotInsteadOfSnapshot && <SnapshotView
|
<SnapshotView
|
||||||
snapshotUrls={snapshotUrls}
|
snapshotUrls={snapshotUrls}
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
testIdAttributeName={testIdAttributeName}
|
testIdAttributeName={testIdAttributeName}
|
||||||
@ -94,11 +83,7 @@ export const SnapshotTabsView: React.FunctionComponent<{
|
|||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocator}
|
||||||
setHighlightedLocator={setHighlightedLocator}
|
setHighlightedLocator={setHighlightedLocator}
|
||||||
/>}
|
/>
|
||||||
{showScreenshotInsteadOfSnapshot && <ScreenshotView
|
|
||||||
action={action}
|
|
||||||
snapshotUrls={snapshotUrls}
|
|
||||||
snapshot={snapshots[snapshotTab]} />}
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -194,38 +179,6 @@ export const SnapshotView: React.FunctionComponent<{
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ScreenshotView: React.FunctionComponent<{
|
|
||||||
action: ActionTraceEvent | undefined,
|
|
||||||
snapshotUrls: SnapshotUrls | undefined,
|
|
||||||
snapshot: Snapshot | undefined,
|
|
||||||
}> = ({ action, snapshotUrls, snapshot }) => {
|
|
||||||
const [snapshotInfo, setSnapshotInfo] = React.useState<SnapshotInfo>({ viewport: kDefaultViewport, url: '' });
|
|
||||||
React.useEffect(() => {
|
|
||||||
fetchSnapshotInfo(snapshotUrls?.snapshotInfoUrl).then(setSnapshotInfo);
|
|
||||||
}, [snapshotUrls?.snapshotInfoUrl]);
|
|
||||||
|
|
||||||
const page = action ? pageForAction(action) : undefined;
|
|
||||||
const screencastFrame = React.useMemo(() => {
|
|
||||||
if (snapshotInfo.wallTime && page?.screencastFrames[0]?.frameSwapWallTime)
|
|
||||||
return findClosest(page.screencastFrames, frame => frame.frameSwapWallTime!, snapshotInfo.wallTime);
|
|
||||||
|
|
||||||
if (snapshotInfo.timestamp && page?.screencastFrames)
|
|
||||||
return findClosest(page.screencastFrames, frame => frame.timestamp, snapshotInfo.timestamp);
|
|
||||||
},
|
|
||||||
[page?.screencastFrames, snapshotInfo.timestamp, snapshotInfo.wallTime]);
|
|
||||||
|
|
||||||
const point = snapshot?.point;
|
|
||||||
|
|
||||||
return <SnapshotWrapper snapshotInfo={snapshotInfo}>
|
|
||||||
{screencastFrame && (
|
|
||||||
<>
|
|
||||||
{point && <ClickPointer point={point} />}
|
|
||||||
<img alt={`Screenshot of ${action?.apiName}`} src={`sha1/${screencastFrame.sha1}`} width={screencastFrame.width} height={screencastFrame.height} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</SnapshotWrapper>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SnapshotWrapper: React.FunctionComponent<React.PropsWithChildren<{
|
const SnapshotWrapper: React.FunctionComponent<React.PropsWithChildren<{
|
||||||
snapshotInfo: SnapshotInfo,
|
snapshotInfo: SnapshotInfo,
|
||||||
}>> = ({ snapshotInfo, children }) => {
|
}>> = ({ snapshotInfo, children }) => {
|
||||||
|
|||||||
@ -55,8 +55,7 @@ export const Workbench: React.FunctionComponent<{
|
|||||||
openPage?: (url: string, target?: string) => Window | any,
|
openPage?: (url: string, target?: string) => Window | any,
|
||||||
onOpenExternally?: (location: modelUtil.SourceLocation) => void,
|
onOpenExternally?: (location: modelUtil.SourceLocation) => void,
|
||||||
revealSource?: boolean,
|
revealSource?: boolean,
|
||||||
showSettings?: boolean,
|
}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, isLive, hideTimeline, status, annotations, inert, openPage, onOpenExternally, revealSource }) => {
|
||||||
}> = ({ model, showSourcesFirst, rootDir, fallbackLocation, isLive, hideTimeline, status, annotations, inert, openPage, onOpenExternally, revealSource, showSettings }) => {
|
|
||||||
const [selectedCallId, setSelectedCallId] = React.useState<string | undefined>(undefined);
|
const [selectedCallId, setSelectedCallId] = React.useState<string | undefined>(undefined);
|
||||||
const [revealedError, setRevealedError] = React.useState<ErrorDescription | undefined>(undefined);
|
const [revealedError, setRevealedError] = React.useState<ErrorDescription | undefined>(undefined);
|
||||||
const [highlightedCallId, setHighlightedCallId] = React.useState<string | undefined>();
|
const [highlightedCallId, setHighlightedCallId] = React.useState<string | undefined>();
|
||||||
@ -68,7 +67,6 @@ export const Workbench: React.FunctionComponent<{
|
|||||||
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
const [highlightedLocator, setHighlightedLocator] = React.useState<string>('');
|
||||||
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
const [selectedTime, setSelectedTime] = React.useState<Boundaries | undefined>();
|
||||||
const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom');
|
const [sidebarLocation, setSidebarLocation] = useSetting<'bottom' | 'right'>('propertiesSidebarLocation', 'bottom');
|
||||||
const showScreenshot = false;
|
|
||||||
|
|
||||||
const setSelectedAction = React.useCallback((action: modelUtil.ActionTraceEventInContext | undefined) => {
|
const setSelectedAction = React.useCallback((action: modelUtil.ActionTraceEventInContext | undefined) => {
|
||||||
setSelectedCallId(action?.callId);
|
setSelectedCallId(action?.callId);
|
||||||
@ -164,7 +162,6 @@ export const Workbench: React.FunctionComponent<{
|
|||||||
id: 'inspector',
|
id: 'inspector',
|
||||||
title: 'Locator',
|
title: 'Locator',
|
||||||
render: () => <InspectorTab
|
render: () => <InspectorTab
|
||||||
showScreenshot={showScreenshot}
|
|
||||||
sdkLanguage={sdkLanguage}
|
sdkLanguage={sdkLanguage}
|
||||||
setIsInspecting={setIsInspecting}
|
setIsInspecting={setIsInspecting}
|
||||||
highlightedLocator={highlightedLocator}
|
highlightedLocator={highlightedLocator}
|
||||||
|
|||||||
@ -165,7 +165,7 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
|||||||
<div className='progress'>
|
<div className='progress'>
|
||||||
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
<div className='inner-progress' style={{ width: progress.total ? (100 * progress.done / progress.total) + '%' : 0 }}></div>
|
||||||
</div>
|
</div>
|
||||||
<Workbench model={model} inert={showFileUploadDropArea} showSettings />
|
<Workbench model={model} inert={showFileUploadDropArea} />
|
||||||
{fileForLocalModeError && <div className='drop-target'>
|
{fileForLocalModeError && <div className='drop-target'>
|
||||||
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
|
<div>Trace Viewer uses Service Workers to show traces. To view trace:</div>
|
||||||
<div style={{ paddingTop: 20 }}>
|
<div style={{ paddingTop: 20 }}>
|
||||||
|
|||||||
@ -1422,24 +1422,6 @@ test('should serve css without content-type', async ({ page, runAndTrace, server
|
|||||||
await expect(snapshotFrame.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)', { timeout: 0 });
|
await expect(snapshotFrame.locator('body')).toHaveCSS('background-color', 'rgb(255, 0, 0)', { timeout: 0 });
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('should allow showing screenshots instead of snapshots', async ({ runAndTrace, page, server }) => {
|
|
||||||
const traceViewer = await runAndTrace(async () => {
|
|
||||||
await page.goto(server.PREFIX + '/one-style.html');
|
|
||||||
await page.waitForTimeout(1000); // ensure we could take a screenshot
|
|
||||||
});
|
|
||||||
|
|
||||||
const screenshot = traceViewer.page.getByAltText(`Screenshot of page.goto`);
|
|
||||||
const snapshot = (await traceViewer.snapshotFrame('page.goto')).owner();
|
|
||||||
await expect(snapshot).toBeVisible();
|
|
||||||
await expect(screenshot).not.toBeVisible();
|
|
||||||
|
|
||||||
await traceViewer.page.getByTitle('Settings').click();
|
|
||||||
await traceViewer.page.getByText('Show screenshot instead of snapshot').setChecked(true);
|
|
||||||
|
|
||||||
await expect(snapshot).not.toBeVisible();
|
|
||||||
await expect(screenshot).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('canvas clipping', async ({ runAndTrace, page, server }) => {
|
test('canvas clipping', async ({ runAndTrace, page, server }) => {
|
||||||
const traceViewer = await runAndTrace(async () => {
|
const traceViewer = await runAndTrace(async () => {
|
||||||
await page.goto(server.PREFIX + '/screenshots/canvas.html#canvas-on-edge');
|
await page.goto(server.PREFIX + '/screenshots/canvas.html#canvas-on-edge');
|
||||||
@ -1466,18 +1448,6 @@ test('canvas clipping in iframe', async ({ runAndTrace, page, server }) => {
|
|||||||
await expect(canvas).toHaveAttribute('title', `Playwright displays canvas contents on a best-effort basis. It doesn't support canvas elements inside an iframe yet. If this impacts your workflow, please open an issue so we can prioritize.`);
|
await expect(canvas).toHaveAttribute('title', `Playwright displays canvas contents on a best-effort basis. It doesn't support canvas elements inside an iframe yet. If this impacts your workflow, please open an issue so we can prioritize.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('should handle case where neither snapshots nor screenshots exist', async ({ runAndTrace, page, server }) => {
|
|
||||||
const traceViewer = await runAndTrace(async () => {
|
|
||||||
await page.goto(server.PREFIX + '/one-style.html');
|
|
||||||
}, { snapshots: false, screenshots: false });
|
|
||||||
|
|
||||||
await traceViewer.page.getByTitle('Settings').click();
|
|
||||||
await traceViewer.page.getByText('Show screenshot instead of snapshot').setChecked(true);
|
|
||||||
|
|
||||||
const screenshot = traceViewer.page.getByAltText(`Screenshot of page.goto > Action`);
|
|
||||||
await expect(screenshot).not.toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should show only one pointer with multilevel iframes', async ({ page, runAndTrace, server, browserName }) => {
|
test('should show only one pointer with multilevel iframes', async ({ page, runAndTrace, server, browserName }) => {
|
||||||
test.fixme(browserName === 'firefox', 'Elements in iframe are not marked');
|
test.fixme(browserName === 'firefox', 'Elements in iframe are not marked');
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user