🧰 WebMCP
New page.webmcp and frame.webmcp give access to the tools that a page registers through the experimental WebMCP browser API, so you can test them like any other part of your app:
const browser = await chromium.launch({ args: ['--enable-features=WebMCP'] });
const page = await browser.newPage();
await page.goto('https://example.com');
for (const tool of await page.webmcp.tools())
console.log(tool.name, tool.description);
const result = await page.webmcp.callTool('add', { a: 2, b: 40 });
console.log(result.content[0].text); // "42"Playwright MCP also supports WebMCP by default, with page-defined tools offered to the agent as webmcp_<tool>. Pass --no-webmcp to opt out.
playwright-cli exposes them as well:
playwright-cli webmcp-list
playwright-cli webmcp-call search_catalog --params '{"query":"cats"}'🎬 Better videos
Videos can now be recorded at a custom frame rate, and the decorations for actions are styled with plain CSS:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
video: {
mode: 'on',
size: { width: 1920, height: 1080 },
fps: 60,
show: {
actions: {
style: {
point: 'width: 20px; height: 20px; border-radius: 50%; background: red',
highlight: 'outline: 2px solid #333; background: rgba(0, 128, 255, .15)',
title: 'font-size: 16px',
},
},
},
},
},
});- New
fpsoption in testOptions.video,recordVideoand
screencast.start(). Firefox and WebKit currently capture up to 25 frames per second. - New
styleoption takes CSS declarations for thepointmarker, the target
highlightand the actiontitle. It replaces thefontSizeoption, which is now deprecated. - The cursor stays visible at the last action point, survives navigations and travels along a natural, eased path.
- Videos are encoded with VP9 instead of VP8, which takes less CPU and produces smaller files of the same or better
quality. - All video options are also available in Playwright MCP and
playwright-cli.
🎯 Test runner
-
New testProject.default option keeps a project in the config without running it by default. You can configure every browser you care about, and let a plain
npx playwright testrun just your favourite one:// playwright.config.ts import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'chromium', use: devices['Desktop Chrome'] }, { name: 'firefox', use: devices['Desktop Firefox'], default: false }, { name: 'webkit', use: devices['Desktop Safari'], default: false }, ], });
npx playwright test # runs chromium only npx playwright test --project=firefox # runs firefox npx playwright test --project="*" # runs all three, for example on CI
-
New
--shufflecommand line option schedules tests in a random order, which helps to find tests that accidentally depend on each other.npx playwright test --shuffle # Running 42 tests using 4 workers, shuffle seed 271828182 # ... # Pass the seed to reproduce the same order. npx playwright test --shuffle 271828182
-
New
lockoption in test.describe.configure() adds test locks to all tests in a file or a group:test.describe.configure({ lock: 'user-settings' });
-
New
typeoption oftoHaveScreenshotin testConfig.expect stores all unnamed screenshots as WebP:// playwright.config.ts export default defineConfig({ expect: { toHaveScreenshot: { type: 'webp' }, }, });
🪆 Locator.within()
New locator.within() combines two locators that you already have, reading in the natural order —
"this button, within that dialog":
const saveButton = page.getByRole('button', { name: 'Save' });
const dialog = page.getByTestId('settings-dialog');
await saveButton.within(dialog).click();Relative locators such as locator.nth() or locator.first() are resolved inside each parent
separately, which makes column-like queries straightforward:
// The third cell of every row, not the third cell in the table.
const thirdColumn = page.getByRole('cell').nth(2).within(page.getByRole('row'));
await expect(thirdColumn).toHaveText(['Apple', 'Banana', 'Cherry']);New APIs
-
New page.getByRef() locates an element by its aria ref, such as
e2, reported by page.ariaSnapshot() in the'ai'mode. -
New option
includeShadowin page.content() and frame.content() serializes open shadow roots as declarative shadow DOM. -
New option
signCountsets the initial signature counter of a virtual credential. credentials.create() and credentials.get() return the currentsignCount, and it is saved and restored together with the credentials in the storage state. -
New fullConfig.filteredProjects lists the projects that were selected to run after applying the
--projectfilter. It is available in global setup and reporters. -
New apiRequestContext.addCookies(), apiRequestContext.cookies() and apiRequestContext.clearCookies() manage cookies of a request context, mirroring the BrowserContext methods:
await request.addCookies([{ name: 'session-id', value: '42', url: 'https://example.com' }]); console.log(await request.cookies('https://example.com')); await request.clearCookies({ name: 'session-id' });
Breaking changes ⚠️
-
screenproperty is now forwarded from the device descriptors. If you use...devices['Desktop Chrome']and alike,window.screenand media queries now see the emulated screen size. You can opt-out by explicitly settingscreentoundefined:// playwright.config.ts export default defineConfig({ use: { ...devices['Desktop Chrome'], screen: undefined, }, });
-
JSX in your test files now follows your
tsconfig.json. Playwright compiles JSX in test files according to thejsx,jsxFactory,jsxFragmentFactoryandjsxImportSourcetsconfig options, and defaults to the automatic runtime fromreact/jsx-runtime. -
--update-snapshots=missingnow passes the test run. Tests that only create missing snapshots now pass in the'missing'mode of testConfig.updateSnapshots, so that CI can generate new snapshots and verify the existing ones in a single run. The mode used when the option is not specified is now called'default'and behaves as before: missing snapshots are written and the test fails. -
Elements inside hidden iframes are considered hidden. Elements inside an iframe that is not visible, for example with
visibility: hidden, are now considered hidden by actions, locator.isVisible() and expect(locator).toBeVisible().
Browser Versions
- Chromium 156.0.8078.4
- Mozilla Firefox 157.0
- WebKit 27.2
This version was also tested against the following stable channels:
- Google Chrome 155
- Microsoft Edge 155