From 1e4645d12a81938b203a046a378fac6fe949497e Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 16 Mar 2026 16:30:44 -0700 Subject: [PATCH] fix(api): make Page.screencast a property instead of a method Page.screencast was accidentally defined as a method. It should be a readonly property, consistent with keyboard, mouse, touchscreen, etc. --- docs/src/api/class-page.md | 13 +++--- docs/src/api/class-screencast.md | 4 +- packages/playwright-client/types/types.d.ts | 43 +++++++++---------- packages/playwright-core/src/client/page.ts | 8 +--- .../tools/dashboard/dashboardController.ts | 8 ++-- packages/playwright-core/types/types.d.ts | 43 +++++++++---------- tests/library/screencast.spec.ts | 24 +++++------ 7 files changed, 68 insertions(+), 75 deletions(-) diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 42c4714c487e5..071af1d84d76a 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -3857,23 +3857,22 @@ Handler function to route the WebSocket. Handler function to route the WebSocket. -## method: Page.screencast +## property: Page.screencast * since: v1.59 * langs: js -- returns: <[Screencast]> +- type: <[Screencast]> -Returns the [Screencast] object associated with this page. +[Screencast] object associated with this page. **Usage** ```js -const screencast = page.screencast(); -screencast.on('screencastFrame', data => { +page.screencast.on('screencastFrame', data => { console.log('received frame, jpeg size:', data.length); }); -await screencast.start(); +await page.screencast.start(); // ... perform actions ... -await screencast.stop(); +await page.screencast.stop(); ``` diff --git a/docs/src/api/class-screencast.md b/docs/src/api/class-screencast.md index b013d1e54ac8d..c5aab7e1af774 100644 --- a/docs/src/api/class-screencast.md +++ b/docs/src/api/class-screencast.md @@ -14,7 +14,7 @@ Emitted for each captured JPEG screencast frame while the screencast is running. **Usage** ```js -const screencast = page.screencast(); +const screencast = page.screencast; screencast.on('screencastframe', ({ data, width, height }) => { console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); require('fs').writeFileSync('frame.jpg', data); @@ -32,7 +32,7 @@ Starts capturing screencast frames. Frames are emitted as [`event: Screencast.sc **Usage** ```js -const screencast = page.screencast(); +const screencast = page.screencast; screencast.on('screencastframe', ({ data, width, height }) => { console.log(`frame ${width}x${height}, size: ${data.length}`); }); diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 2449c65b39211..10be6cbb19f2e 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -4157,24 +4157,6 @@ export interface Page { */ routeWebSocket(url: string|RegExp|URLPattern|((url: URL) => boolean), handler: ((websocketroute: WebSocketRoute) => Promise|any)): Promise; - /** - * Returns the [Screencast](https://playwright.dev/docs/api/class-screencast) object associated with this page. - * - * **Usage** - * - * ```js - * const screencast = page.screencast(); - * screencast.on('screencastFrame', data => { - * console.log('received frame, jpeg size:', data.length); - * }); - * await screencast.start(); - * // ... perform actions ... - * await screencast.stop(); - * ``` - * - */ - screencast(): Screencast; - /** * Returns the buffer with the captured screenshot. * @param options @@ -5311,6 +5293,23 @@ export interface Page { */ request: APIRequestContext; + /** + * [Screencast](https://playwright.dev/docs/api/class-screencast) object associated with this page. + * + * **Usage** + * + * ```js + * page.screencast.on('screencastFrame', data => { + * console.log('received frame, jpeg size:', data.length); + * }); + * await page.screencast.start(); + * // ... perform actions ... + * await page.screencast.stop(); + * ``` + * + */ + screencast: Screencast; + touchscreen: Touchscreen; [Symbol.asyncDispose](): Promise; @@ -21648,7 +21647,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); * require('fs').writeFileSync('frame.jpg', data); @@ -21682,7 +21681,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); * require('fs').writeFileSync('frame.jpg', data); @@ -21726,7 +21725,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); * require('fs').writeFileSync('frame.jpg', data); @@ -21752,7 +21751,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, size: ${data.length}`); * }); diff --git a/packages/playwright-core/src/client/page.ts b/packages/playwright-core/src/client/page.ts index 143b34822283b..3f58b2bb37921 100644 --- a/packages/playwright-core/src/client/page.ts +++ b/packages/playwright-core/src/client/page.ts @@ -95,12 +95,12 @@ export class Page extends ChannelOwner implements api.Page readonly request: APIRequestContext; readonly touchscreen: Touchscreen; readonly clock: Clock; + readonly screencast: Screencast; readonly _bindings = new Map any>(); readonly _timeoutSettings: TimeoutSettings; private _video: Video; - private _screencast: Screencast; readonly _opener: Page | null; private _closeReason: string | undefined; _closeWasCalled: boolean = false; @@ -135,7 +135,7 @@ export class Page extends ChannelOwner implements api.Page this._closed = initializer.isClosed; this._opener = Page.fromNullable(initializer.opener); this._video = new Video(this, this._connection, initializer.video ? Artifact.from(initializer.video) : undefined); - this._screencast = new Screencast(this); + this.screencast = new Screencast(this); this._channel.on('bindingCall', ({ binding }) => this._onBinding(BindingCall.from(binding))); this._channel.on('close', () => this._onClose()); @@ -285,10 +285,6 @@ export class Page extends ChannelOwner implements api.Page return this._video; } - screencast(): Screencast { - return this._screencast; - } - async pickLocator(): Promise { const { selector } = await this._channel.pickLocator({}); return this.locator(selector); diff --git a/packages/playwright-core/src/tools/dashboard/dashboardController.ts b/packages/playwright-core/src/tools/dashboard/dashboardController.ts index d8cf80556622c..aae12854225fa 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardController.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardController.ts @@ -179,7 +179,7 @@ export class DashboardConnection implements Transport, DashboardChannel { if (this.selectedPage) { this._pageListeners.forEach(d => d.dispose()); this._pageListeners = []; - await this.selectedPage.screencast().stop(); + await this.selectedPage.screencast.stop(); } this.selectedPage = page; @@ -199,11 +199,11 @@ export class DashboardConnection implements Transport, DashboardChannel { if (frame === page.mainFrame()) this._sendTabList(); }), - eventsHelper.addEventListener(page.screencast(), 'screencastframe', ({ data }) => this._writeFrame(data, page.viewportSize()?.width ?? 0, page.viewportSize()?.height ?? 0)) + eventsHelper.addEventListener(page.screencast, 'screencastframe', ({ data }) => this._writeFrame(data, page.viewportSize()?.width ?? 0, page.viewportSize()?.height ?? 0)) ); const maxSize = { width: 1280, height: 800 }; - await page.screencast().start({ maxSize }); + await page.screencast.start({ maxSize }); } private _deselectPage() { @@ -211,7 +211,7 @@ export class DashboardConnection implements Transport, DashboardChannel { return; this._pageListeners.forEach(d => d.dispose()); this._pageListeners = []; - this.selectedPage.screencast().stop().catch(() => {}); + this.selectedPage.screencast.stop().catch(() => {}); this.selectedPage = null; this._lastFrameData = null; this._lastViewportSize = null; diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 2449c65b39211..10be6cbb19f2e 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -4157,24 +4157,6 @@ export interface Page { */ routeWebSocket(url: string|RegExp|URLPattern|((url: URL) => boolean), handler: ((websocketroute: WebSocketRoute) => Promise|any)): Promise; - /** - * Returns the [Screencast](https://playwright.dev/docs/api/class-screencast) object associated with this page. - * - * **Usage** - * - * ```js - * const screencast = page.screencast(); - * screencast.on('screencastFrame', data => { - * console.log('received frame, jpeg size:', data.length); - * }); - * await screencast.start(); - * // ... perform actions ... - * await screencast.stop(); - * ``` - * - */ - screencast(): Screencast; - /** * Returns the buffer with the captured screenshot. * @param options @@ -5311,6 +5293,23 @@ export interface Page { */ request: APIRequestContext; + /** + * [Screencast](https://playwright.dev/docs/api/class-screencast) object associated with this page. + * + * **Usage** + * + * ```js + * page.screencast.on('screencastFrame', data => { + * console.log('received frame, jpeg size:', data.length); + * }); + * await page.screencast.start(); + * // ... perform actions ... + * await page.screencast.stop(); + * ``` + * + */ + screencast: Screencast; + touchscreen: Touchscreen; [Symbol.asyncDispose](): Promise; @@ -21648,7 +21647,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); * require('fs').writeFileSync('frame.jpg', data); @@ -21682,7 +21681,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); * require('fs').writeFileSync('frame.jpg', data); @@ -21726,7 +21725,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, jpeg size: ${data.length}`); * require('fs').writeFileSync('frame.jpg', data); @@ -21752,7 +21751,7 @@ export interface Screencast { * **Usage** * * ```js - * const screencast = page.screencast(); + * const screencast = page.screencast; * screencast.on('screencastframe', ({ data, width, height }) => { * console.log(`frame ${width}x${height}, size: ${data.length}`); * }); diff --git a/tests/library/screencast.spec.ts b/tests/library/screencast.spec.ts index 2b2d207f4fd5b..ade493c914b27 100644 --- a/tests/library/screencast.spec.ts +++ b/tests/library/screencast.spec.ts @@ -26,14 +26,14 @@ test('screencast.start emits screencastframe events', async ({ browser, server, const page = await context.newPage(); const frames: { data: Buffer }[] = []; - page.screencast().on('screencastframe', frame => frames.push(frame)); + page.screencast.on('screencastframe', frame => frames.push(frame)); const maxSize = { width: 500, height: 400 }; - await page.screencast().start({ maxSize }); + await page.screencast.start({ maxSize }); await page.goto(server.EMPTY_PAGE); await page.evaluate(() => document.body.style.backgroundColor = 'red'); await rafraf(page, 100); - await page.screencast().stop(); + await page.screencast.stop(); expect(frames.length).toBeGreaterThan(0); for (const frame of frames) { @@ -56,10 +56,10 @@ test('start throws if already running', async ({ browser, trace }) => { const context = await browser.newContext({ viewport: size }); const page = await context.newPage(); - await page.screencast().start({ maxSize: size }); - await expect(page.screencast().start({ maxSize: { width: 320, height: 240 } })).rejects.toThrow('Screencast is already running'); + await page.screencast.start({ maxSize: size }); + await expect(page.screencast.start({ maxSize: { width: 320, height: 240 } })).rejects.toThrow('Screencast is already running'); - await page.screencast().stop(); + await page.screencast.stop(); await context.close(); }); @@ -69,11 +69,11 @@ test('start allows restart with different options after stop', async ({ browser, const context = await browser.newContext({ viewport: { width: 500, height: 400 } }); const page = await context.newPage(); - await page.screencast().start({ maxSize: { width: 500, height: 400 } }); - await page.screencast().stop(); + await page.screencast.start({ maxSize: { width: 500, height: 400 } }); + await page.screencast.stop(); // Different options should succeed once the previous screencast is stopped. - await page.screencast().start({ maxSize: { width: 320, height: 240 } }); - await page.screencast().stop(); + await page.screencast.start({ maxSize: { width: 320, height: 240 } }); + await page.screencast.stop(); await context.close(); }); @@ -85,7 +85,7 @@ test('start throws when video recording is running with different params', async const page = await context.newPage(); await page.video().start({ size: videoSize }); - await expect(page.screencast().start({ maxSize: { width: 320, height: 240 } })).rejects.toThrow('Screencast is already running with different options'); + await expect(page.screencast.start({ maxSize: { width: 320, height: 240 } })).rejects.toThrow('Screencast is already running with different options'); await page.video().stop(); await context.close(); @@ -95,7 +95,7 @@ test('video.start does not emit screencastframe events', async ({ page, server, test.skip(trace === 'on', 'trace=on enables screencast frame events'); const frames = []; - page.screencast().on('screencastframe', frame => frames.push(frame)); + page.screencast.on('screencastframe', frame => frames.push(frame)); await page.video().start({ size: { width: 320, height: 240 } }); await page.goto(server.EMPTY_PAGE);