From 8dcb1c3af8415e3e5165b5c15015333e8e934219 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 16 Mar 2026 16:33:55 -0700 Subject: [PATCH 01/11] chore(tools): ban `as any` via eslint and fix existing usages (#39709) --- eslint.config.mjs | 7 +++++++ .../playwright-core/src/tools/backend/browserBackend.ts | 7 ++++--- packages/playwright-core/src/tools/backend/evaluate.ts | 1 + packages/playwright-core/src/tools/backend/response.ts | 4 +--- packages/playwright-core/src/tools/backend/screenshot.ts | 1 + packages/playwright-core/src/tools/backend/sessionLog.ts | 5 +---- packages/playwright-core/src/tools/backend/tab.ts | 2 ++ packages/playwright-core/src/tools/backend/tracing.ts | 2 ++ packages/playwright-core/src/tools/backend/utils.ts | 2 ++ packages/playwright-core/src/tools/backend/verify.ts | 3 ++- packages/playwright-core/src/tools/cli-daemon/command.ts | 2 +- .../playwright-core/src/tools/dashboard/dashboardApp.ts | 5 ++++- .../src/tools/dashboard/dashboardController.ts | 5 ++++- packages/playwright-core/src/tools/mcp/browserFactory.ts | 1 + 14 files changed, 33 insertions(+), 14 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index e9875fbfae903..23dc500594841 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -367,6 +367,13 @@ export default [ }], }, ], + "no-restricted-syntax": [ + "error", + { + selector: "TSAsExpression > TSAnyKeyword", + message: "Avoid 'as any' — risk of accidentally casting to client interfaces. Use a precise type or add an eslint-disable with justification.", + }, + ], }, }, { diff --git a/packages/playwright-core/src/tools/backend/browserBackend.ts b/packages/playwright-core/src/tools/backend/browserBackend.ts index 6ab579763ad66..222b6e26cbaa1 100644 --- a/packages/playwright-core/src/tools/backend/browserBackend.ts +++ b/packages/playwright-core/src/tools/backend/browserBackend.ts @@ -51,7 +51,7 @@ export class BrowserBackend implements ServerBackend { await this._context?.dispose().catch(e => debug('pw:tools:error')(e)); } - async callTool(name: string, rawArguments: mcpServer.CallToolRequest['params']['arguments']) { + async callTool(name: string, rawArguments: mcpServer.CallToolRequest['params']['arguments'] & { _meta?: Record } = {}): Promise { const tool = this._tools.find(tool => tool.schema.name === name)!; if (!tool) { return { @@ -59,8 +59,9 @@ export class BrowserBackend implements ServerBackend { isError: true, }; } - const parsedArguments = tool.schema.inputSchema.parse(rawArguments || {}) as any; - const cwd = rawArguments?._meta && typeof rawArguments?._meta === 'object' && (rawArguments._meta as any)?.cwd; + // eslint-disable-next-line no-restricted-syntax + const parsedArguments = tool.schema.inputSchema.parse(rawArguments) as any; + const cwd = rawArguments._meta?.cwd; const context = this._context!; const response = new Response(context, name, parsedArguments, cwd); context.setRunningTool(name); diff --git a/packages/playwright-core/src/tools/backend/evaluate.ts b/packages/playwright-core/src/tools/backend/evaluate.ts index 6e0ce373f4a4b..30cf32f48f406 100644 --- a/packages/playwright-core/src/tools/backend/evaluate.ts +++ b/packages/playwright-core/src/tools/backend/evaluate.ts @@ -50,6 +50,7 @@ const evaluate = defineTabTool({ } await tab.waitForCompletion(async () => { + // eslint-disable-next-line no-restricted-syntax const func = new Function() as any; func.toString = () => params.function; const result = locator?.locator ? await locator?.locator.evaluate(func) : await tab.page.evaluate(func); diff --git a/packages/playwright-core/src/tools/backend/response.ts b/packages/playwright-core/src/tools/backend/response.ts index d686353c66c9a..c3603481385d6 100644 --- a/packages/playwright-core/src/tools/backend/response.ts +++ b/packages/playwright-core/src/tools/backend/response.ts @@ -276,9 +276,7 @@ function trimMiddle(text: string, maxLength: number) { * Replaces lone surrogates with U+FFFD using String.prototype.toWellFormed(). */ function sanitizeUnicode(text: string): string { - if ((String.prototype as any).toWellFormed) - return text.toWellFormed(); - return text; + return text.toWellFormed?.() ?? text; } function parseSections(text: string): Map { diff --git a/packages/playwright-core/src/tools/backend/screenshot.ts b/packages/playwright-core/src/tools/backend/screenshot.ts index b2273965a1ac9..4cfa23e7608f6 100644 --- a/packages/playwright-core/src/tools/backend/screenshot.ts +++ b/packages/playwright-core/src/tools/backend/screenshot.ts @@ -86,6 +86,7 @@ export function scaleImageToFitMessage(buffer: Buffer, imageType: 'png' | 'jpeg' const width = image.width * shrink | 0; const height = image.height * shrink | 0; const scaledImage = scaleImageToSize(image, { width, height }); + // eslint-disable-next-line no-restricted-syntax return imageType === 'png' ? PNG.sync.write(scaledImage as any) : jpegjs.encode(scaledImage, 80).data; } diff --git a/packages/playwright-core/src/tools/backend/sessionLog.ts b/packages/playwright-core/src/tools/backend/sessionLog.ts index 8e213031d270b..f4497a2ccdb15 100644 --- a/packages/playwright-core/src/tools/backend/sessionLog.ts +++ b/packages/playwright-core/src/tools/backend/sessionLog.ts @@ -41,10 +41,7 @@ export class SessionLog { } logResponse(toolName: string, toolArgs: Record, responseObject: any) { - const parsed = parseResponse(responseObject) as any; - if (parsed) - delete parsed.text; - + const parsed = { ...parseResponse(responseObject), text: undefined }; const lines: string[] = ['']; lines.push( `### Tool call: ${toolName}`, diff --git a/packages/playwright-core/src/tools/backend/tab.ts b/packages/playwright-core/src/tools/backend/tab.ts index be8b823e983ff..89d4241044813 100644 --- a/packages/playwright-core/src/tools/backend/tab.ts +++ b/packages/playwright-core/src/tools/backend/tab.ts @@ -132,6 +132,7 @@ export class Tab extends EventEmitter { void this._downloadStarted(download); }), ]; + // eslint-disable-next-line no-restricted-syntax (page as any)[tabSymbol] = this; const wallTime = Date.now(); this._consoleLog = new LogFile(this.context, wallTime, 'console', 'Console'); @@ -147,6 +148,7 @@ export class Tab extends EventEmitter { } static forPage(page: playwright.Page): Tab | undefined { + // eslint-disable-next-line no-restricted-syntax return (page as any)[tabSymbol]; } diff --git a/packages/playwright-core/src/tools/backend/tracing.ts b/packages/playwright-core/src/tools/backend/tracing.ts index 9b1eeeba15177..2d8090c0d4323 100644 --- a/packages/playwright-core/src/tools/backend/tracing.ts +++ b/packages/playwright-core/src/tools/backend/tracing.ts @@ -43,6 +43,7 @@ const tracingStart = defineTool({ response.addFileLink('Action log', `${tracesDir}/${name}.trace`); response.addFileLink('Network log', `${tracesDir}/${name}.network`); response.addFileLink('Resources', `${tracesDir}/resources`); + // eslint-disable-next-line no-restricted-syntax (browserContext.tracing as any)[traceLegendSymbol] = { tracesDir, name }; }, }); @@ -61,6 +62,7 @@ const tracingStop = defineTool({ handle: async (context, params, response) => { const browserContext = await context.ensureBrowserContext(); await browserContext.tracing.stop(); + // eslint-disable-next-line no-restricted-syntax const traceLegend = (browserContext.tracing as any)[traceLegendSymbol]; response.addTextResult(`Trace recording stopped.`); response.addFileLink('Trace', `${traceLegend.tracesDir}/${traceLegend.name}.trace`); diff --git a/packages/playwright-core/src/tools/backend/utils.ts b/packages/playwright-core/src/tools/backend/utils.ts index b235de4d9b83a..2b1a4a659943b 100644 --- a/packages/playwright-core/src/tools/backend/utils.ts +++ b/packages/playwright-core/src/tools/backend/utils.ts @@ -59,7 +59,9 @@ export function eventWaiter(page: playwright.Page, event: string, timeout: nu const disposables: (() => void)[] = []; const eventPromise = new Promise((resolve, reject) => { + // eslint-disable-next-line no-restricted-syntax page.on(event as any, resolve as any); + // eslint-disable-next-line no-restricted-syntax disposables.push(() => page.off(event as any, resolve as any)); }); diff --git a/packages/playwright-core/src/tools/backend/verify.ts b/packages/playwright-core/src/tools/backend/verify.ts index b1535756793d3..60b65272a4764 100644 --- a/packages/playwright-core/src/tools/backend/verify.ts +++ b/packages/playwright-core/src/tools/backend/verify.ts @@ -18,6 +18,7 @@ import { z } from '../../mcpBundle'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils'; import { defineTabTool } from './tool'; +import type * as playwright from '../../..'; const verifyElement = defineTabTool({ capability: 'testing', @@ -34,7 +35,7 @@ const verifyElement = defineTabTool({ handle: async (tab, params, response) => { for (const frame of tab.page.frames()) { - const locator = frame.getByRole(params.role as any, { name: params.accessibleName }); + const locator = frame.getByRole(params.role as Parameters[0], { name: params.accessibleName }); if (await locator.count() > 0) { const resolved = await locator.toCode(); response.addCode(`await expect(page.${resolved}).toBeVisible();`); diff --git a/packages/playwright-core/src/tools/cli-daemon/command.ts b/packages/playwright-core/src/tools/cli-daemon/command.ts index 04de065bada0e..bd60bf7bf74bb 100644 --- a/packages/playwright-core/src/tools/cli-daemon/command.ts +++ b/packages/playwright-core/src/tools/cli-daemon/command.ts @@ -65,7 +65,7 @@ function zodParse(schema: zodType.ZodAny, data: unknown, type: 'option' | 'argum return schema.parse(data); } catch (e) { throw new Error((e as zodType.ZodError).issues.map(issue => { - const keys: string[] = (issue as any).keys || ['']; + const keys: string[] = issue.code === 'unrecognized_keys' ? issue.keys : ['']; const props = keys.map(key => [...issue.path, key].filter(Boolean).join('.')); return props.map(prop => { const label = type === 'option' ? `'--${prop}' option` : `'${prop}' argument`; diff --git a/packages/playwright-core/src/tools/dashboard/dashboardApp.ts b/packages/playwright-core/src/tools/dashboard/dashboardApp.ts index becbc59342c32..f2d8d336c660c 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardApp.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardApp.ts @@ -216,7 +216,8 @@ async function launchApp(appName: string) { const image = await fs.promises.readFile(path.join(__dirname, 'appIcon.png')); // This is local Playwright, so I can access private methods. - await (page as any)._setDockTile(image); + // eslint-disable-next-line no-restricted-syntax -- it is not essential, can regress. + await (page as any)._setDockTile?.(image); await syncLocalStorageWithSettings(page, appName); return { context, page }; } @@ -238,7 +239,9 @@ export async function syncLocalStorageWithSettings(page: api.Page, appName: stri if (window.top !== window) return; Object.entries(settings).map(([k, v]) => localStorage[k] = v); + // eslint-disable-next-line no-restricted-syntax (window as any).saveSettings = () => { + // eslint-disable-next-line no-restricted-syntax (window as any)._saveSerializedSettings(JSON.stringify({ ...localStorage })); }; })})(${settings}); diff --git a/packages/playwright-core/src/tools/dashboard/dashboardController.ts b/packages/playwright-core/src/tools/dashboard/dashboardController.ts index a20cb91420c79..d8cf80556622c 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardController.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardController.ts @@ -106,6 +106,7 @@ export class DashboardConnection implements Transport, DashboardChannel { async dispatch(method: string, params: any): Promise { await this._initPromise; + // eslint-disable-next-line no-restricted-syntax return (this as any)[method]?.(params); } @@ -260,10 +261,12 @@ export class DashboardConnection implements Transport, DashboardChannel { } private _pageId(p: api.Page): string { + // eslint-disable-next-line no-restricted-syntax -- _guid is very conservative. return (p as any)._guid; } private async _devtoolsUrl(page: api.Page) { + // eslint-disable-next-line no-restricted-syntax -- cdpPort is not in the public LaunchOptions type, fine if regresses. const cdpPort = (this._browserDescriptor.browser.launchOptions as any).cdpPort; if (cdpPort) return new URL(`http://localhost:${cdpPort}/devtools/`); @@ -327,7 +330,7 @@ export class CDPConnection implements Transport { await this._initializePromise; if (!this._rawSession) throw new Error('CDP session is not initialized'); - return await this._rawSession.send(method as any, params); + return await this._rawSession.send(method as Parameters[0], params); } onclose() { diff --git a/packages/playwright-core/src/tools/mcp/browserFactory.ts b/packages/playwright-core/src/tools/mcp/browserFactory.ts index 26a16054941c1..6b3f9b2c958dd 100644 --- a/packages/playwright-core/src/tools/mcp/browserFactory.ts +++ b/packages/playwright-core/src/tools/mcp/browserFactory.ts @@ -151,6 +151,7 @@ async function createUserDataDir(config: FullConfig, clientInfo: ClientInfo) { async function injectCdpPort(browserConfig: FullConfig['browser']) { if (browserConfig.browserName === 'chromium') + // eslint-disable-next-line no-restricted-syntax (browserConfig.launchOptions as any).cdpPort = await findFreePort(); } From 4e00ab73c394d6d542c77c987aa4384f38088418 Mon Sep 17 00:00:00 2001 From: Holger Benl Date: Tue, 17 Mar 2026 00:39:28 +0100 Subject: [PATCH 02/11] chore(bidi): fix console.warn() message type (#39702) --- packages/playwright-core/src/server/bidi/bidiPage.ts | 3 ++- tests/bidi/expectations/moz-firefox-nightly-page.txt | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index 3911b4517a5c1..f94fe48c1a52c 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -292,7 +292,8 @@ export class BidiPage implements PageDelegate { const callFrame = params.stackTrace?.callFrames[0]; const location = callFrame ?? { url: '', lineNumber: 1, columnNumber: 1 }; - this._page.addConsoleMessage(null, entry.method, entry.args.map(arg => createHandle(context, arg)), location, undefined, params.timestamp); + const type = entry.method === 'warn' ? 'warning' : entry.method; + this._page.addConsoleMessage(null, type, entry.args.map(arg => createHandle(context, arg)), location, undefined, params.timestamp); } private async _onFileDialogOpened(params: bidi.Input.FileDialogInfo) { diff --git a/tests/bidi/expectations/moz-firefox-nightly-page.txt b/tests/bidi/expectations/moz-firefox-nightly-page.txt index b0237ad33689a..aaec3f6391531 100644 --- a/tests/bidi/expectations/moz-firefox-nightly-page.txt +++ b/tests/bidi/expectations/moz-firefox-nightly-page.txt @@ -93,7 +93,6 @@ page/page-emulate-media.spec.ts › should work during navigation [fail] page/page-event-console.spec.ts › should have location for console API calls [fail] page/page-event-console.spec.ts › should trigger correct Log [timeout] page/page-event-console.spec.ts › should work @smoke [flaky] -page/page-event-console.spec.ts › should work for different console API calls [fail] page/page-event-request.spec.ts › should report requests and responses handled by service worker [fail] page/page-event-request.spec.ts › should report requests and responses handled by service worker with routing [fail] page/page-expose-function.spec.ts › should work with setContent [timeout] From e1decbf18246514c90b925cae9e5003c7b53a8fd Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 16 Mar 2026 17:18:52 -0700 Subject: [PATCH 03/11] chore(mcp): remove unused install-browser command (#39694) --- .../playwright-core/src/tools/mcp/cli-stub.ts | 7 +------ .../playwright-core/src/tools/mcp/program.ts | 16 ---------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/playwright-core/src/tools/mcp/cli-stub.ts b/packages/playwright-core/src/tools/mcp/cli-stub.ts index 36de64d4eb949..269212dec7ca5 100644 --- a/packages/playwright-core/src/tools/mcp/cli-stub.ts +++ b/packages/playwright-core/src/tools/mcp/cli-stub.ts @@ -15,14 +15,9 @@ */ import { program } from '../../utilsBundle'; -import { decorateMCPCommand, decorateMCPInstallBrowserCommand } from './program'; +import { decorateMCPCommand } from './program'; const packageJSON = require('../../../package.json'); const p = program.version('Version ' + packageJSON.version).name('Playwright MCP'); decorateMCPCommand(p); - -const installBrowserCommand = p.command('install-browser'); -installBrowserCommand.description('Install browser for Playwright MCP'); -decorateMCPInstallBrowserCommand(installBrowserCommand); - void program.parseAsync(process.argv); diff --git a/packages/playwright-core/src/tools/mcp/program.ts b/packages/playwright-core/src/tools/mcp/program.ts index e0026dc891f46..794bb8f2cc32e 100644 --- a/packages/playwright-core/src/tools/mcp/program.ts +++ b/packages/playwright-core/src/tools/mcp/program.ts @@ -142,19 +142,3 @@ export function decorateMCPCommand(command: Command) { await mcpServer.start(factory, config.server); }); } - -export function decorateMCPInstallBrowserCommand(command: Command) { - command - .description('ensure browsers necessary for this version of Playwright are installed') - .option('--with-deps', 'install system dependencies for browsers') - .option('--dry-run', 'do not execute installation, only print information') - .option('--list', 'prints list of browsers from all playwright installations') - .option('--force', 'force reinstall of already installed browsers') - .option('--only-shell', 'only install headless shell when installing chromium') - .option('--no-shell', 'do not install chromium headless shell') - .action(async options => { - const argv = process.argv.map(arg => arg === 'install-browser' ? 'install' : arg); - const { program: mainProgram } = await import('../../cli/program'); - mainProgram.parse(argv); - }); -} From e90b25bbb5ce78064b6f1a8800796072961b877d Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Mon, 16 Mar 2026 17:19:03 -0700 Subject: [PATCH 04/11] fix(api): make Page.screencast a property instead of a method (#39711) --- 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); From b0898ee1cf058532968bc125b24fd09ece1fd018 Mon Sep 17 00:00:00 2001 From: "microsoft-playwright-automation[bot]" <203992400+microsoft-playwright-automation[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:23:32 -0700 Subject: [PATCH 05/11] test: roll stable-test-runner to 1.59.0-alpha-2026-03-16 (#39699) --- .../stable-test-runner/package-lock.json | 24 +++++++++---------- .../stable-test-runner/package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/playwright-test/stable-test-runner/package-lock.json b/tests/playwright-test/stable-test-runner/package-lock.json index 639b37ea79646..954d34dfc5e2e 100644 --- a/tests/playwright-test/stable-test-runner/package-lock.json +++ b/tests/playwright-test/stable-test-runner/package-lock.json @@ -5,16 +5,16 @@ "packages": { "": { "dependencies": { - "@playwright/test": "^1.59.0-alpha-2026-03-09" + "@playwright/test": "^1.59.0-alpha-2026-03-16" } }, "node_modules/@playwright/test": { - "version": "1.59.0-alpha-2026-03-09", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0-alpha-2026-03-09.tgz", - "integrity": "sha512-PXeDcWsvJgqV5dTBAcCX23DrMDDuAOu4kGOqiGvVBLcV+AqGZl06Bp+dmYjA77an43fe1Xhn8ZftHnsfnqtqpA==", + "version": "1.59.0-alpha-2026-03-16", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.0-alpha-2026-03-16.tgz", + "integrity": "sha512-zJLmTNdYYVcOrGAqgNIrIVcOys89V9X4Lui/0+6JP8C8Co5wsyMjRVIxitRkl8jI/tA+Xh8o70pLn4ll/AYENQ==", "license": "Apache-2.0", "dependencies": { - "playwright": "1.59.0-alpha-2026-03-09" + "playwright": "1.59.0-alpha-2026-03-16" }, "bin": { "playwright": "cli.js" @@ -38,12 +38,12 @@ } }, "node_modules/playwright": { - "version": "1.59.0-alpha-2026-03-09", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0-alpha-2026-03-09.tgz", - "integrity": "sha512-S7idgqb6cxRDrJJEXcRuXNC6W/vgJj5DNMvGGX/lbqmko/Kqshs1Ab1dDg2sUVUPs8Z2vxDqIgOcTjSPN++Abw==", + "version": "1.59.0-alpha-2026-03-16", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.0-alpha-2026-03-16.tgz", + "integrity": "sha512-wtwBbAhQSPtJblmaHxomfvK5R27ddUKlq5Ikv7x59FW1w69MMJ4A/kTPI7gDVXZiJ559kP6r5oDrVAHxBRSr2g==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.59.0-alpha-2026-03-09" + "playwright-core": "1.59.0-alpha-2026-03-16" }, "bin": { "playwright": "cli.js" @@ -56,9 +56,9 @@ } }, "node_modules/playwright-core": { - "version": "1.59.0-alpha-2026-03-09", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0-alpha-2026-03-09.tgz", - "integrity": "sha512-Ff3X9IJPIxCyOGmv1C+YzbWEm6Z1Lg6/EEPf1864EaBw30P+dEC+YJGp01FAxYqT8Eev0VXYApv1qwM/e6q9sQ==", + "version": "1.59.0-alpha-2026-03-16", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.0-alpha-2026-03-16.tgz", + "integrity": "sha512-tp/atMQoHl4bJPaE1dDr7hH9zSake93gBhz3qu5cuf3SCHL6cQC6/NTe5gcecAt9ccp99QalHhuqi+CAwC9X1g==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" diff --git a/tests/playwright-test/stable-test-runner/package.json b/tests/playwright-test/stable-test-runner/package.json index 07494039ca9f9..62b00d5e1cbbf 100644 --- a/tests/playwright-test/stable-test-runner/package.json +++ b/tests/playwright-test/stable-test-runner/package.json @@ -1,6 +1,6 @@ { "private": true, "dependencies": { - "@playwright/test": "^1.59.0-alpha-2026-03-09" + "@playwright/test": "^1.59.0-alpha-2026-03-16" } } From 40b9da3f7145fe22d09f4bdf77eb8a577457681a Mon Sep 17 00:00:00 2001 From: Mani Movassagh <74688476+manimovassagh@users.noreply.github.com> Date: Tue, 17 Mar 2026 02:32:15 +0100 Subject: [PATCH 06/11] docs(ci): use origin/ prefix with --only-changed in GitHub Actions snippet (#39698) --- docs/src/ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/ci.md b/docs/src/ci.md index 585295b8d98bf..5b319041a6d1e 100644 --- a/docs/src/ci.md +++ b/docs/src/ci.md @@ -440,7 +440,7 @@ jobs: - name: Install Playwright Browsers run: npx playwright install --with-deps - name: Run changed Playwright tests - run: npx playwright test --only-changed=$GITHUB_BASE_REF + run: npx playwright test --only-changed=origin/$GITHUB_BASE_REF if: github.event_name == 'pull_request' - name: Run Playwright tests run: npx playwright test From 2247c2769cc86ade87f3e32a3ae45f86bf752258 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:33:17 -0700 Subject: [PATCH 07/11] chore(deps): bump black from 24.3.0 to 26.3.1 in /utils/doclint/linting-code-snippets/python (#39647) --- utils/doclint/linting-code-snippets/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/doclint/linting-code-snippets/python/requirements.txt b/utils/doclint/linting-code-snippets/python/requirements.txt index 70f3034c8d80d..eb1f300ebb07e 100644 --- a/utils/doclint/linting-code-snippets/python/requirements.txt +++ b/utils/doclint/linting-code-snippets/python/requirements.txt @@ -1 +1 @@ -black==24.3.0 +black==26.3.1 From e50a59ca1c2addc62b92f5c252b12c4ed0668195 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 16 Mar 2026 21:02:26 -0700 Subject: [PATCH 08/11] test(network): remove flaky cached IMG waitForResponse test (#39713) --- tests/page/page-wait-for-response.spec.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/page/page-wait-for-response.spec.ts b/tests/page/page-wait-for-response.spec.ts index c6e0bd329640f..e6734925609cd 100644 --- a/tests/page/page-wait-for-response.spec.ts +++ b/tests/page/page-wait-for-response.spec.ts @@ -116,17 +116,3 @@ it('should work with no timeout', async ({ page, server }) => { ]); expect(response.url()).toBe(server.PREFIX + '/digits/2.png'); }); - -it('should work with re-rendered cached IMG elements', async ({ page, server, browserName, isBidi }) => { - it.fixme(browserName === 'webkit'); - it.fixme(browserName === 'firefox' && !isBidi); - await page.goto(server.EMPTY_PAGE); - await page.setContent(``); - await page.$eval('img', img => img.remove()); - const [response] = await Promise.all([ - page.waitForRequest(/pptr/), - page.waitForResponse(/pptr/), - page.setContent(``) - ]); - expect(response.url()).toBe(server.PREFIX + '/pptr.png'); -}); From b02edbad57f34ba52b5469a819c0ecb85a86bf39 Mon Sep 17 00:00:00 2001 From: Alex Tumanov Date: Mon, 16 Mar 2026 23:40:08 -0500 Subject: [PATCH 09/11] fix(cli): correct mousewheel delta mapping (#39716) --- packages/playwright-core/src/tools/cli-daemon/commands.ts | 6 +++--- tests/mcp/cli-mouse.spec.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/playwright-core/src/tools/cli-daemon/commands.ts b/packages/playwright-core/src/tools/cli-daemon/commands.ts index 69dfc9439f17d..7f7d1f9cc0102 100644 --- a/packages/playwright-core/src/tools/cli-daemon/commands.ts +++ b/packages/playwright-core/src/tools/cli-daemon/commands.ts @@ -212,11 +212,11 @@ const mouseWheel = declareCommand({ description: 'Scroll mouse wheel', category: 'mouse', args: z.object({ - dx: numberArg.describe('Y delta'), - dy: numberArg.describe('X delta'), + dx: numberArg.describe('X delta'), + dy: numberArg.describe('Y delta'), }), toolName: 'browser_mouse_wheel', - toolParams: ({ dx: deltaY, dy: deltaX }) => ({ deltaY, deltaX }), + toolParams: ({ dx: deltaX, dy: deltaY }) => ({ deltaX, deltaY }), }); // Core diff --git a/tests/mcp/cli-mouse.spec.ts b/tests/mcp/cli-mouse.spec.ts index 5ce183c07e8d4..59060c747f698 100644 --- a/tests/mcp/cli-mouse.spec.ts +++ b/tests/mcp/cli-mouse.spec.ts @@ -45,5 +45,5 @@ test('mousewheel', async ({ cli, server }) => { await cli('mousewheel', '10', '5'); - await expect.poll(() => cli('snapshot').then(result => result.snapshot)).toContain('wheel 5 10'); + await expect.poll(() => cli('snapshot').then(result => result.snapshot)).toContain('wheel 10 5'); }); From a92363b680552249cfb731085f8461bebedb0dd1 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Mon, 16 Mar 2026 21:55:35 -0700 Subject: [PATCH 10/11] fix(page): make page.title() return empty string instead of throwing during navigation (#39658) --- packages/playwright-core/src/server/frames.ts | 13 +++++++++++-- .../src/tools/dashboard/dashboardController.ts | 3 +-- tests/page/page-basic.spec.ts | 12 ++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index a09b41d050cff..3f7f2cc5fd88b 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -1566,8 +1566,17 @@ export class Frame extends SdkObject { } async title(): Promise { - const context = await this._utilityContext(); - return context.evaluate(() => document.title); + try { + return await this.raceAgainstEvaluationStallingEvents(async () => { + const context = await this._utilityContext(); + return await context.evaluate(() => document.title); + }); + } catch { + const url = this.pendingDocument()?.request?.url(); + if (url) + return `Loading ${url}`; + return ''; + } } async rafrafTimeout(progress: Progress, timeout: number): Promise { diff --git a/packages/playwright-core/src/tools/dashboard/dashboardController.ts b/packages/playwright-core/src/tools/dashboard/dashboardController.ts index aae12854225fa..fd8bb68e1fa9c 100644 --- a/packages/playwright-core/src/tools/dashboard/dashboardController.ts +++ b/packages/playwright-core/src/tools/dashboard/dashboardController.ts @@ -244,8 +244,7 @@ export class DashboardConnection implements Transport, DashboardChannel { return []; const devtoolsUrl = await this._devtoolsUrl(pages[0]); return await Promise.all(pages.map(async page => { - // page.title() throws on navigation. - const title = await page.title().catch(() => undefined) || `Loading ${page.url()}`; + const title = await page.title(); return { pageId: this._pageId(page), title, diff --git a/tests/page/page-basic.spec.ts b/tests/page/page-basic.spec.ts index e8d9bc5a8eee0..cf923d747a469 100644 --- a/tests/page/page-basic.spec.ts +++ b/tests/page/page-basic.spec.ts @@ -85,6 +85,18 @@ it('page.title should return the page title', async ({ page, server }) => { expect(await page.title()).toBe('Woof-Woof'); }); +it('page.title should not throw during navigation', async ({ page, server }) => { + await page.setContent('hello'); + const promise = page.goto(server.PREFIX + '/title.html'); + const [titleOrError] = await Promise.all([ + page.title().catch(e => e), + promise, + ]); + expect(typeof titleOrError).toBe('string'); + expect(titleOrError).toMatch(/^(Loading http.*title.html|hello|Woof-Woof)$/); + await expect(page).toHaveTitle('Woof-Woof'); +}); + it('page.close should work with window.close', async function({ page }) { const newPagePromise = page.waitForEvent('popup'); await page.evaluate(() => window['newPage'] = window.open('about:blank')); From 0a871c9db89c047f899f14b482001cc8fa30dda9 Mon Sep 17 00:00:00 2001 From: Holger Benl Date: Tue, 17 Mar 2026 06:02:06 +0100 Subject: [PATCH 11/11] chore(bidi): wait for pages to close before returning from context.close() (#39588) --- packages/playwright-core/src/server/bidi/bidiBrowser.ts | 1 + packages/playwright-core/src/server/page.ts | 6 +++--- tests/bidi/expectations/moz-firefox-nightly-library.txt | 2 +- tests/bidi/expectations/moz-firefox-nightly-page.txt | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/playwright-core/src/server/bidi/bidiBrowser.ts b/packages/playwright-core/src/server/bidi/bidiBrowser.ts index 9a4ac5b7cb924..f3444410bbdfe 100644 --- a/packages/playwright-core/src/server/bidi/bidiBrowser.ts +++ b/packages/playwright-core/src/server/bidi/bidiBrowser.ts @@ -505,6 +505,7 @@ export class BidiBrowserContext extends BrowserContext { await this._browser._browserSession.send('browser.removeUserContext', { userContext: this._browserContextId }); + await Promise.all(this._bidiPages().map(bidiPage => bidiPage._page.closedPromise)); this._browser._contexts.delete(this._browserContextId); } diff --git a/packages/playwright-core/src/server/page.ts b/packages/playwright-core/src/server/page.ts index 894f1a49dce45..4ace511c0a313 100644 --- a/packages/playwright-core/src/server/page.ts +++ b/packages/playwright-core/src/server/page.ts @@ -157,7 +157,7 @@ export class Page extends SdkObject { static Events = PageEvent; private _closedState: 'open' | 'closing' | 'closed' = 'open'; - private _closedPromise = new ManualPromise(); + readonly closedPromise = new ManualPromise(); private _initialized: Page | Error | undefined; private _initializedPromise = new ManualPromise(); private _consoleMessages: ConsoleMessage[] = []; @@ -284,7 +284,7 @@ export class Page extends SdkObject { this._closedState = 'closed'; this.emit(Page.Events.Close); this.browserContext.emit(BrowserContext.Events.PageClosed, this); - this._closedPromise.resolve(); + this.closedPromise.resolve(); this.instrumentation.onPageClose(this); this.openScope.close(new TargetClosedError(this.closeReason())); } @@ -797,7 +797,7 @@ export class Page extends SdkObject { await this.delegate.closePage(runBeforeUnload).catch(e => debugLogger.log('error', e)); } if (!runBeforeUnload) - await this._closedPromise; + await this.closedPromise; } isClosed(): boolean { diff --git a/tests/bidi/expectations/moz-firefox-nightly-library.txt b/tests/bidi/expectations/moz-firefox-nightly-library.txt index 9a8352445676a..2202fd34be4fa 100644 --- a/tests/bidi/expectations/moz-firefox-nightly-library.txt +++ b/tests/bidi/expectations/moz-firefox-nightly-library.txt @@ -1,7 +1,6 @@ library/agent-perform.spec.ts › retrieve a secret [timeout] library/browsercontext-add-cookies.spec.ts › should set cookies with SameSite attribute and no secure attribute [fail] library/browsercontext-basic.spec.ts › fetch with keepalive should throw when offline [fail] -library/browsercontext-basic.spec.ts › should close all belonging pages once closing context [fail] library/browsercontext-basic.spec.ts › should disable javascript [fail] library/browsercontext-basic.spec.ts › should emulate media in cross-process iframe [fail] library/browsercontext-basic.spec.ts › should emulate media in popup [fail] @@ -156,6 +155,7 @@ library/popup.spec.ts › should use viewport size from window features [timeout library/role-utils.spec.ts › axe-core accessible-text [timeout] library/role-utils.spec.ts › wpt accname #2 [timeout] library/route-web-socket.spec.ts › should emit close upon frame detach [timeout] +library/screencast.spec.ts › screencast.start emits screencastframe events [fail] library/screenshot.spec.ts › page screenshot › should work with device scale factor and scale:css [fail] library/screenshot.spec.ts › page screenshot › should work with device scale factor, clip and scale:css [fail] library/selector-generator.spec.ts › selector generator › should work in dynamic iframes without navigation [fail] diff --git a/tests/bidi/expectations/moz-firefox-nightly-page.txt b/tests/bidi/expectations/moz-firefox-nightly-page.txt index aaec3f6391531..07177c1dbf1df 100644 --- a/tests/bidi/expectations/moz-firefox-nightly-page.txt +++ b/tests/bidi/expectations/moz-firefox-nightly-page.txt @@ -107,6 +107,7 @@ page/page-history.spec.ts › goBack/goForward should work with bfcache-able pag page/page-history.spec.ts › page.goBack should work for file urls [timeout] page/page-history.spec.ts › regression test for issue 20791 [flaky] page/page-keyboard.spec.ts › insertText should only emit input event [fail] +page/page-keyboard.spec.ts › should press audio and media control keys [fail] page/page-keyboard.spec.ts › should press Enter [fail] page/page-keyboard.spec.ts › should send a character with insertText [fail] page/page-mouse.spec.ts › should always round down [fail]