From e6f5ce9044b0bee8e89cc071a24d5436f9a7bcb1 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 9 Apr 2021 08:27:55 +0800 Subject: [PATCH] chore: allow running multiple snapshotters for tests (#6147) --- src/server/snapshot/snapshotter.ts | 62 ++++++++++++---------- src/server/snapshot/snapshotterInjected.ts | 14 ++--- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/server/snapshot/snapshotter.ts b/src/server/snapshot/snapshotter.ts index 0a5850d32c..ebc8fb491a 100644 --- a/src/server/snapshot/snapshotter.ts +++ b/src/server/snapshot/snapshotter.ts @@ -20,7 +20,7 @@ import * as network from '../network'; import { helper, RegisteredListener } from '../helper'; import { debugLogger } from '../../utils/debugLogger'; import { Frame } from '../frames'; -import { SnapshotData, frameSnapshotStreamer, kSnapshotBinding, kSnapshotStreamer } from './snapshotterInjected'; +import { SnapshotData, frameSnapshotStreamer } from './snapshotterInjected'; import { calculateSha1, createGuid, monotonicTime } from '../../utils/utils'; import { FrameSnapshot, ResourceSnapshot } from './snapshotTypes'; import { ElementHandle } from '../dom'; @@ -41,6 +41,8 @@ export class Snapshotter { private _delegate: SnapshotterDelegate; private _eventListeners: RegisteredListener[] = []; private _interval = 0; + private _snapshotStreamer: string; + private _snapshotBinding: string; constructor(context: BrowserContext, delegate: SnapshotterDelegate) { this._context = context; @@ -50,10 +52,13 @@ export class Snapshotter { this._eventListeners = [ 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() { - await this._context.exposeBinding(kSnapshotBinding, false, (source, data: SnapshotData) => { + await this._context.exposeBinding(this._snapshotBinding, false, (source, data: SnapshotData) => { const snapshot: FrameSnapshot = { snapshotName: data.snapshotName, pageId: source.page.uniqueId, @@ -79,7 +84,7 @@ export class Snapshotter { } this._delegate.onFrameSnapshot(snapshot); }); - const initScript = '(' + frameSnapshotStreamer.toString() + ')()'; + const initScript = `(${frameSnapshotStreamer})("${this._snapshotStreamer}", "${this._snapshotBinding}")`; await this._context._doAddInitScript(initScript); const frames = []; for (const page of this._context.pages()) @@ -95,7 +100,7 @@ export class Snapshotter { captureSnapshot(page: Page, snapshotName: string, element?: ElementHandle) { // 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.setAttribute('__playwright_target__', snapshotName); }, snapshotName); @@ -111,15 +116,14 @@ export class Snapshotter { const frames = []; for (const page of this._context.pages()) 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) { const processNewFrame = (frame: Frame) => { - annotateFrameHierarchy(frame); - setIntervalInFrame(frame, this._interval); - // FIXME: make addInitScript work for pages w/ setContent. - const initScript = '(' + frameSnapshotStreamer.toString() + ')()'; + this._annotateFrameHierarchy(frame); + this._setIntervalInFrame(frame, this._interval); + const initScript = `(${frameSnapshotStreamer})("${this._snapshotStreamer}", "${this._snapshotBinding}")`; frame._existingMainContext()?.rawEvaluate(initScript).catch(debugExceptionHandler); }; for (const frame of page.frames()) @@ -128,7 +132,7 @@ export class Snapshotter { // Push streamer interval on navigation. this._eventListeners.push(helper.addEventListener(page, Page.Events.InternalFrameNavigatedToNewDocument, frame => { - setIntervalInFrame(frame, this._interval); + this._setIntervalInFrame(frame, this._interval); })); // Capture resources. @@ -182,27 +186,27 @@ export class Snapshotter { if (body) this._delegate.onBlob({ sha1: responseSha1, buffer: body }); } -} -async function setIntervalInFrame(frame: Frame, interval: number) { - const context = frame._existingMainContext(); - await context?.evaluate(({ kSnapshotStreamer, interval }) => { - (window as any)[kSnapshotStreamer].setSnapshotInterval(interval); - }, { kSnapshotStreamer, interval }).catch(debugExceptionHandler); -} + private async _setIntervalInFrame(frame: Frame, interval: number) { + const context = frame._existingMainContext(); + await context?.evaluate(({ snapshotStreamer, interval }) => { + (window as any)[snapshotStreamer].setSnapshotInterval(interval); + }, { snapshotStreamer: this._snapshotStreamer, interval }).catch(debugExceptionHandler); + } -async function annotateFrameHierarchy(frame: Frame) { - try { - const frameElement = await frame.frameElement(); - const parent = frame.parentFrame(); - if (!parent) - return; - const context = await parent._mainContext(); - await context?.evaluate(({ kSnapshotStreamer, frameElement, frameId }) => { - (window as any)[kSnapshotStreamer].markIframe(frameElement, frameId); - }, { kSnapshotStreamer, frameElement, frameId: frame.uniqueId }); - frameElement.dispose(); - } catch (e) { + private async _annotateFrameHierarchy(frame: Frame) { + try { + const frameElement = await frame.frameElement(); + const parent = frame.parentFrame(); + if (!parent) + return; + const context = await parent._mainContext(); + await context?.evaluate(({ snapshotStreamer, frameElement, frameId }) => { + (window as any)[snapshotStreamer].markIframe(frameElement, frameId); + }, { snapshotStreamer: this._snapshotStreamer, frameElement, frameId: frame.uniqueId }); + frameElement.dispose(); + } catch (e) { + } } } diff --git a/src/server/snapshot/snapshotterInjected.ts b/src/server/snapshot/snapshotterInjected.ts index 4ab3cba88a..268c370ba3 100644 --- a/src/server/snapshot/snapshotterInjected.ts +++ b/src/server/snapshot/snapshotterInjected.ts @@ -31,15 +31,9 @@ export type SnapshotData = { collectionTime: number, }; -export const kSnapshotStreamer = '__playwright_snapshot_streamer_'; -export const kSnapshotBinding = '__playwright_snapshot_binding_'; - -export function frameSnapshotStreamer() { +export function frameSnapshotStreamer(snapshotStreamer: string, snapshotBinding: string) { // Communication with Playwright. - const kSnapshotStreamer = '__playwright_snapshot_streamer_'; - const kSnapshotBinding = '__playwright_snapshot_binding_'; - - if ((window as any)[kSnapshotStreamer]) + if ((window as any)[snapshotStreamer]) return; // Attributes present in the snapshot. @@ -186,7 +180,7 @@ export function frameSnapshotStreamer() { try { const snapshot = this._captureSnapshot(snapshotName); if (snapshot) - (window as any)[kSnapshotBinding](snapshot); + (window as any)[snapshotBinding](snapshot); } catch (e) { } if (this._interval) @@ -458,5 +452,5 @@ export function frameSnapshotStreamer() { } } - (window as any)[kSnapshotStreamer] = new Streamer(); + (window as any)[snapshotStreamer] = new Streamer(); }