diff --git a/docs/src/api/class-screencast.md b/docs/src/api/class-screencast.md index 7b28daf8b1ff3..4d1ba1843ea8e 100644 --- a/docs/src/api/class-screencast.md +++ b/docs/src/api/class-screencast.md @@ -22,7 +22,9 @@ await page.screencast.stop(); ```js // Capture frames await page.screencast.start({ - onFrame: ({ data }) => console.log(`frame size: ${data.length}`), + onFrame: ({ data, viewportWidth, viewportHeight }) => { + console.log(`frame size: ${data.length} (${viewportWidth}x${viewportHeight})`); + }, size: { width: 800, height: 600 }, }); // ... perform actions ... @@ -34,8 +36,10 @@ await page.screencast.stop(); - `onFrame` <[function]\([Object]\): [Promise]> - alias: ScreencastFrame - `data` <[Buffer]> JPEG-encoded frame data. + - `viewportWidth` <[int]> Width of the page viewport at the time the frame was captured. + - `viewportHeight` <[int]> Height of the page viewport at the time the frame was captured. -Callback that receives JPEG-encoded frame data. +Callback that receives JPEG-encoded frame data along with the page viewport size at the time of capture. ### option: Screencast.start.path * since: v1.59 diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 4a33756068ddb..db9b9739919a0 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -16557,7 +16557,9 @@ export interface Screencast { * ```js * // Capture frames * await page.screencast.start({ - * onFrame: ({ data }) => console.log(`frame size: ${data.length}`), + * onFrame: ({ data, viewportWidth, viewportHeight }) => { + * console.log(`frame size: ${data.length} (${viewportWidth}x${viewportHeight})`); + * }, * size: { width: 800, height: 600 }, * }); * // ... perform actions ... @@ -16567,7 +16569,7 @@ export interface Screencast { * @param options */ start(options?: { - onFrame?: (frame: { data: Buffer }) => Promise|any; + onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any; path?: string; size?: { width: number; diff --git a/packages/playwright-core/src/client/screencast.ts b/packages/playwright-core/src/client/screencast.ts index 83c21db702457..e4a775688e20d 100644 --- a/packages/playwright-core/src/client/screencast.ts +++ b/packages/playwright-core/src/client/screencast.ts @@ -24,17 +24,17 @@ export class Screencast implements api.Screencast { private _page: Page; private _started = false; private _savePath: string | undefined; - private _onFrame: ((frame: { data: Buffer }) => Promise) | null = null; + private _onFrame: ((frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise) | null = null; private _artifact: Artifact | undefined; constructor(page: Page) { this._page = page; - this._page._channel.on('screencastFrame', ({ data }) => { - void this._onFrame?.({ data }); + this._page._channel.on('screencastFrame', ({ data, viewportWidth, viewportHeight }) => { + void this._onFrame?.({ data, viewportWidth, viewportHeight }); }); } - async start(options: { onFrame?: (frame: { data: Buffer }) => Promise|any, path?: string, size?: { width: number, height: number }, quality?: number } = {}): Promise { + async start(options: { onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any, path?: string, size?: { width: number, height: number }, quality?: number } = {}): Promise { if (this._started) throw new Error('Screencast is already started'); this._started = true; diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 831dfc6f19d24..8c0a3fcb23b84 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -2189,6 +2189,8 @@ scheme.PageRouteEvent = tObject({ }); scheme.PageScreencastFrameEvent = tObject({ data: tBinary, + viewportWidth: tInt, + viewportHeight: tInt, }); scheme.PageWebSocketRouteEvent = tObject({ webSocketRoute: tChannel(['WebSocketRoute']), diff --git a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts index 867476914df60..56dc88c2cecf1 100644 --- a/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/pageDispatcher.ts @@ -396,7 +396,7 @@ export class PageDispatcher extends Dispatcher { - this._dispatchEvent('screencastFrame', { data: frame.buffer }); + this._dispatchEvent('screencastFrame', { data: frame.buffer, viewportWidth: frame.viewportWidth, viewportHeight: frame.viewportHeight }); }, dispose: () => {}, size: params.size, diff --git a/packages/playwright-core/src/tools/dashboard/dashboardController.ts b/packages/playwright-core/src/tools/dashboard/dashboardController.ts index d67269d184e0a..79e0c503c5474 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardController.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardController.ts @@ -448,11 +448,10 @@ class AttachedPage { private async _startScreencast(page: api.Page) { await page.screencast.start({ - onFrame: ({ data }: { data: Buffer }) => { + onFrame: ({ data, viewportWidth, viewportHeight }) => { if (this._disposed) return; - const vp = page.viewportSize(); - this._owner.emitFrame(data.toString('base64'), vp?.width ?? 0, vp?.height ?? 0); + this._owner.emitFrame(data.toString('base64'), viewportWidth, viewportHeight); }, size: { width: 1280, height: 800 }, ...(this._recordingPath ? { path: this._recordingPath } : {}), diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 4a33756068ddb..db9b9739919a0 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -16557,7 +16557,9 @@ export interface Screencast { * ```js * // Capture frames * await page.screencast.start({ - * onFrame: ({ data }) => console.log(`frame size: ${data.length}`), + * onFrame: ({ data, viewportWidth, viewportHeight }) => { + * console.log(`frame size: ${data.length} (${viewportWidth}x${viewportHeight})`); + * }, * size: { width: 800, height: 600 }, * }); * // ... perform actions ... @@ -16567,7 +16569,7 @@ export interface Screencast { * @param options */ start(options?: { - onFrame?: (frame: { data: Buffer }) => Promise|any; + onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any; path?: string; size?: { width: number; diff --git a/packages/protocol/spec/page.yml b/packages/protocol/spec/page.yml index 4d9b1793b94ec..a5e6e16488e81 100644 --- a/packages/protocol/spec/page.yml +++ b/packages/protocol/spec/page.yml @@ -652,6 +652,8 @@ Page: screencastFrame: parameters: data: binary + viewportWidth: int + viewportHeight: int webSocketRoute: parameters: diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index edb828825bf8a..b1aede0f72880 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -3908,6 +3908,8 @@ export type PageRouteEvent = { }; export type PageScreencastFrameEvent = { data: Binary, + viewportWidth: number, + viewportHeight: number, }; export type PageWebSocketRouteEvent = { webSocketRoute: WebSocketRouteChannel, diff --git a/tests/library/screencast.spec.ts b/tests/library/screencast.spec.ts index 24e345ca0740d..867766fb2d163 100644 --- a/tests/library/screencast.spec.ts +++ b/tests/library/screencast.spec.ts @@ -52,6 +52,29 @@ test('screencast.start delivers frames via onFrame callback', async ({ browser, await context.close(); }); +test('onFrame receives viewport size', async ({ browser, server, trace }) => { + test.skip(trace === 'on', 'trace=on has different screencast image configuration'); + const context = await browser.newContext({ viewport: { width: 1000, height: 400 } }); + const page = await context.newPage(); + + const frames: { viewportWidth: number, viewportHeight: number }[] = []; + await page.screencast.start({ + onFrame: ({ viewportWidth, viewportHeight }) => frames.push({ viewportWidth, viewportHeight }), + size: { width: 500, height: 400 }, + }); + await page.goto(server.EMPTY_PAGE); + await ensureSomeFrames(page); + await page.screencast.stop(); + + expect(frames.length).toBeGreaterThan(0); + for (const frame of frames) { + expect(frame.viewportWidth).toBe(1000); + expect(frame.viewportHeight).toBe(400); + } + + await context.close(); +}); + test('start throws if screencast is already started', async ({ browser }) => { const context = await browser.newContext({ viewport: { width: 500, height: 400 } }); const page = await context.newPage(); diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index dba016b023342..f7f10b66a3b3b 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -245,7 +245,7 @@ export interface WebSocketRoute { export interface Screencast { start(options?: { - onFrame?: (frame: { data: Buffer }) => Promise|any; + onFrame?: (frame: { data: Buffer, viewportWidth: number, viewportHeight: number }) => Promise|any; path?: string; size?: { width: number;