mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: allow running multiple snapshotters for tests (#6147)
This commit is contained in:
parent
db09275d89
commit
e6f5ce9044
@ -20,7 +20,7 @@ import * as network from '../network';
|
|||||||
import { helper, RegisteredListener } from '../helper';
|
import { helper, RegisteredListener } from '../helper';
|
||||||
import { debugLogger } from '../../utils/debugLogger';
|
import { debugLogger } from '../../utils/debugLogger';
|
||||||
import { Frame } from '../frames';
|
import { Frame } from '../frames';
|
||||||
import { SnapshotData, frameSnapshotStreamer, kSnapshotBinding, kSnapshotStreamer } from './snapshotterInjected';
|
import { SnapshotData, frameSnapshotStreamer } from './snapshotterInjected';
|
||||||
import { calculateSha1, createGuid, monotonicTime } from '../../utils/utils';
|
import { calculateSha1, createGuid, monotonicTime } from '../../utils/utils';
|
||||||
import { FrameSnapshot, ResourceSnapshot } from './snapshotTypes';
|
import { FrameSnapshot, ResourceSnapshot } from './snapshotTypes';
|
||||||
import { ElementHandle } from '../dom';
|
import { ElementHandle } from '../dom';
|
||||||
@ -41,6 +41,8 @@ export class Snapshotter {
|
|||||||
private _delegate: SnapshotterDelegate;
|
private _delegate: SnapshotterDelegate;
|
||||||
private _eventListeners: RegisteredListener[] = [];
|
private _eventListeners: RegisteredListener[] = [];
|
||||||
private _interval = 0;
|
private _interval = 0;
|
||||||
|
private _snapshotStreamer: string;
|
||||||
|
private _snapshotBinding: string;
|
||||||
|
|
||||||
constructor(context: BrowserContext, delegate: SnapshotterDelegate) {
|
constructor(context: BrowserContext, delegate: SnapshotterDelegate) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
@ -50,10 +52,13 @@ export class Snapshotter {
|
|||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
helper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this)),
|
helper.addEventListener(this._context, BrowserContext.Events.Page, this._onPage.bind(this)),
|
||||||
];
|
];
|
||||||
|
const guid = createGuid();
|
||||||
|
this._snapshotStreamer = '__playwright_snapshot_streamer_' + guid;
|
||||||
|
this._snapshotBinding = '__playwright_snapshot_binding_' + guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
await this._context.exposeBinding(kSnapshotBinding, false, (source, data: SnapshotData) => {
|
await this._context.exposeBinding(this._snapshotBinding, false, (source, data: SnapshotData) => {
|
||||||
const snapshot: FrameSnapshot = {
|
const snapshot: FrameSnapshot = {
|
||||||
snapshotName: data.snapshotName,
|
snapshotName: data.snapshotName,
|
||||||
pageId: source.page.uniqueId,
|
pageId: source.page.uniqueId,
|
||||||
@ -79,7 +84,7 @@ export class Snapshotter {
|
|||||||
}
|
}
|
||||||
this._delegate.onFrameSnapshot(snapshot);
|
this._delegate.onFrameSnapshot(snapshot);
|
||||||
});
|
});
|
||||||
const initScript = '(' + frameSnapshotStreamer.toString() + ')()';
|
const initScript = `(${frameSnapshotStreamer})("${this._snapshotStreamer}", "${this._snapshotBinding}")`;
|
||||||
await this._context._doAddInitScript(initScript);
|
await this._context._doAddInitScript(initScript);
|
||||||
const frames = [];
|
const frames = [];
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
@ -95,7 +100,7 @@ export class Snapshotter {
|
|||||||
|
|
||||||
captureSnapshot(page: Page, snapshotName: string, element?: ElementHandle) {
|
captureSnapshot(page: Page, snapshotName: string, element?: ElementHandle) {
|
||||||
// This needs to be sync, as in not awaiting for anything before we issue the command.
|
// This needs to be sync, as in not awaiting for anything before we issue the command.
|
||||||
const expression = `window[${JSON.stringify(kSnapshotStreamer)}].captureSnapshot(${JSON.stringify(snapshotName)})`;
|
const expression = `window["${this._snapshotStreamer}"].captureSnapshot(${JSON.stringify(snapshotName)})`;
|
||||||
element?.callFunctionNoReply((element: Element, snapshotName: string) => {
|
element?.callFunctionNoReply((element: Element, snapshotName: string) => {
|
||||||
element.setAttribute('__playwright_target__', snapshotName);
|
element.setAttribute('__playwright_target__', snapshotName);
|
||||||
}, snapshotName);
|
}, snapshotName);
|
||||||
@ -111,15 +116,14 @@ export class Snapshotter {
|
|||||||
const frames = [];
|
const frames = [];
|
||||||
for (const page of this._context.pages())
|
for (const page of this._context.pages())
|
||||||
frames.push(...page.frames());
|
frames.push(...page.frames());
|
||||||
await Promise.all(frames.map(frame => setIntervalInFrame(frame, interval)));
|
await Promise.all(frames.map(frame => this._setIntervalInFrame(frame, interval)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onPage(page: Page) {
|
private _onPage(page: Page) {
|
||||||
const processNewFrame = (frame: Frame) => {
|
const processNewFrame = (frame: Frame) => {
|
||||||
annotateFrameHierarchy(frame);
|
this._annotateFrameHierarchy(frame);
|
||||||
setIntervalInFrame(frame, this._interval);
|
this._setIntervalInFrame(frame, this._interval);
|
||||||
// FIXME: make addInitScript work for pages w/ setContent.
|
const initScript = `(${frameSnapshotStreamer})("${this._snapshotStreamer}", "${this._snapshotBinding}")`;
|
||||||
const initScript = '(' + frameSnapshotStreamer.toString() + ')()';
|
|
||||||
frame._existingMainContext()?.rawEvaluate(initScript).catch(debugExceptionHandler);
|
frame._existingMainContext()?.rawEvaluate(initScript).catch(debugExceptionHandler);
|
||||||
};
|
};
|
||||||
for (const frame of page.frames())
|
for (const frame of page.frames())
|
||||||
@ -128,7 +132,7 @@ export class Snapshotter {
|
|||||||
|
|
||||||
// Push streamer interval on navigation.
|
// Push streamer interval on navigation.
|
||||||
this._eventListeners.push(helper.addEventListener(page, Page.Events.InternalFrameNavigatedToNewDocument, frame => {
|
this._eventListeners.push(helper.addEventListener(page, Page.Events.InternalFrameNavigatedToNewDocument, frame => {
|
||||||
setIntervalInFrame(frame, this._interval);
|
this._setIntervalInFrame(frame, this._interval);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Capture resources.
|
// Capture resources.
|
||||||
@ -182,29 +186,29 @@ export class Snapshotter {
|
|||||||
if (body)
|
if (body)
|
||||||
this._delegate.onBlob({ sha1: responseSha1, buffer: body });
|
this._delegate.onBlob({ sha1: responseSha1, buffer: body });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function setIntervalInFrame(frame: Frame, interval: number) {
|
private async _setIntervalInFrame(frame: Frame, interval: number) {
|
||||||
const context = frame._existingMainContext();
|
const context = frame._existingMainContext();
|
||||||
await context?.evaluate(({ kSnapshotStreamer, interval }) => {
|
await context?.evaluate(({ snapshotStreamer, interval }) => {
|
||||||
(window as any)[kSnapshotStreamer].setSnapshotInterval(interval);
|
(window as any)[snapshotStreamer].setSnapshotInterval(interval);
|
||||||
}, { kSnapshotStreamer, interval }).catch(debugExceptionHandler);
|
}, { snapshotStreamer: this._snapshotStreamer, interval }).catch(debugExceptionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function annotateFrameHierarchy(frame: Frame) {
|
private async _annotateFrameHierarchy(frame: Frame) {
|
||||||
try {
|
try {
|
||||||
const frameElement = await frame.frameElement();
|
const frameElement = await frame.frameElement();
|
||||||
const parent = frame.parentFrame();
|
const parent = frame.parentFrame();
|
||||||
if (!parent)
|
if (!parent)
|
||||||
return;
|
return;
|
||||||
const context = await parent._mainContext();
|
const context = await parent._mainContext();
|
||||||
await context?.evaluate(({ kSnapshotStreamer, frameElement, frameId }) => {
|
await context?.evaluate(({ snapshotStreamer, frameElement, frameId }) => {
|
||||||
(window as any)[kSnapshotStreamer].markIframe(frameElement, frameId);
|
(window as any)[snapshotStreamer].markIframe(frameElement, frameId);
|
||||||
}, { kSnapshotStreamer, frameElement, frameId: frame.uniqueId });
|
}, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.uniqueId });
|
||||||
frameElement.dispose();
|
frameElement.dispose();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function debugExceptionHandler(e: Error) {
|
function debugExceptionHandler(e: Error) {
|
||||||
// console.error(e);
|
// console.error(e);
|
||||||
|
|||||||
@ -31,15 +31,9 @@ export type SnapshotData = {
|
|||||||
collectionTime: number,
|
collectionTime: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const kSnapshotStreamer = '__playwright_snapshot_streamer_';
|
export function frameSnapshotStreamer(snapshotStreamer: string, snapshotBinding: string) {
|
||||||
export const kSnapshotBinding = '__playwright_snapshot_binding_';
|
|
||||||
|
|
||||||
export function frameSnapshotStreamer() {
|
|
||||||
// Communication with Playwright.
|
// Communication with Playwright.
|
||||||
const kSnapshotStreamer = '__playwright_snapshot_streamer_';
|
if ((window as any)[snapshotStreamer])
|
||||||
const kSnapshotBinding = '__playwright_snapshot_binding_';
|
|
||||||
|
|
||||||
if ((window as any)[kSnapshotStreamer])
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Attributes present in the snapshot.
|
// Attributes present in the snapshot.
|
||||||
@ -186,7 +180,7 @@ export function frameSnapshotStreamer() {
|
|||||||
try {
|
try {
|
||||||
const snapshot = this._captureSnapshot(snapshotName);
|
const snapshot = this._captureSnapshot(snapshotName);
|
||||||
if (snapshot)
|
if (snapshot)
|
||||||
(window as any)[kSnapshotBinding](snapshot);
|
(window as any)[snapshotBinding](snapshot);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
if (this._interval)
|
if (this._interval)
|
||||||
@ -458,5 +452,5 @@ export function frameSnapshotStreamer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(window as any)[kSnapshotStreamer] = new Streamer();
|
(window as any)[snapshotStreamer] = new Streamer();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user