Playwright 1.65 is out, and it is one of the more practical releases of the year for JavaScript and TypeScript teams. The test runner now runs under Bun, there is a new failures-only reporter for noisy CI logs, a built-in exclusive test lock, and a list of breaking changes you should check before upgrading. This tutorial walks through each change with copy-paste config and code, based on the official release notes.
Upgrade first
Update the package and the browsers it drives:
npm install -D @playwright/test@latest
npx playwright install
Playwright 1.65 bundles Chromium 158.0.8127.18, Firefox 159.0 and WebKit 27.2, and was also tested against stable Chrome 157 and Edge 156. Important: Playwright no longer tests on Node.js 20. Use Node.js 22, 24 or 26. The official Docker images now include Node.js 26.
1. Run your tests with Bun
The test runner can now run under Bun, which transpiles TypeScript test files itself, so Playwright skips Babel and its compilation cache. You must pass --bun, because without it bunx still runs Playwright on Node.js:
bunx --bun playwright test
The Playwright team runs its own test-runner tests under Bun 1.4.3 on Linux. There are real limitations, so read these before switching CI:
- No watch mode. UI mode does not re-run tests when files change, and
--only-changeddoes not follow imports. - Bun loads modules itself, so
--tsconfigand the JSX options intsconfig.jsonhave no effect, and custom ESM loaders are not supported. - Stack traces have no async frames, and API names lose the class name (
clickinstead oflocator.click), which changes some step titles and error messages. - Console output from tests does not appear in traces or UI mode.
A sensible approach is to add a Bun job next to your Node.js job, compare duration and results for a week, and only then decide.
2. Only show what failed
Large suites produce long logs where the one failure is hard to find. The new failures reporter prints failures and successful retries as they happen, then a summary, with no progress output. The HTML, JSON and JUnit reporters also get an onlyFailures option that keeps only tests with unexpected outcomes, flaky tests and interrupted tests:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: process.env.CI
? [
['failures'],
['html', { onlyFailures: true, open: 'never' }],
['junit', { outputFile: 'results/junit.xml', onlyFailures: true }],
]
: [['list']],
});
Smaller HTML reports are faster to upload as CI artifacts, and JUnit files with only failures are easier to read in pull request annotations.
3. Run one test with nothing else in parallel
Some tests must not overlap with others, such as a test that restarts a shared server or resets a database. Playwright 1.65 adds a built-in -exclusive- lock. The test runs only when no other tests are running, while everything else stays parallel:
import { test, expect } from '@playwright/test';
test('restart the API server', { lock: '-exclusive-' }, async ({ request }) => {
// No other test runs while this one is running.
const res = await request.post('/admin/restart');
expect(res.ok()).toBeTruthy();
});
test('list products', async ({ page }) => {
await page.goto('/products');
await expect(page.getByRole('heading', { name: 'Products' })).toBeVisible();
});
This replaces workarounds like moving such tests into a separate project with workers: 1.
4. Smaller API changes
test.skip(),test.fixme(),test.fail()andtest.slow()called with arguments now accept only a description, which is recorded in the test annotation:test.skip('Not implemented yet');- A new
PLAYWRIGHT_TRANSFORM_CACHE_DIRenvironment variable sets where transpiled test files are stored, useful on Linux machines shared by several users. cdpSession.send()accepts newtimeoutandsignaloptions.- A new
testRun.mode()returns a suite’s execution mode duringreporter.preprocess().
5. Breaking changes to check before you merge
TypeScript class fields now follow useDefineForClassFields
This is the one most likely to break page objects. Playwright now respects useDefineForClassFields from your tsconfig.json, just like tsc. It is on for ES2022+ targets and when no target is set. A field initializer that reads a constructor parameter property now fails because the initializer runs first:
// Breaks in 1.65:
class LoginPage {
readonly submit = this.page.getByRole('button', { name: 'Submit' });
constructor(private readonly page: Page) {}
}
// Fix: initialise in the constructor
class LoginPage {
readonly submit: Locator;
constructor(private readonly page: Page) {
this.submit = page.getByRole('button', { name: 'Submit' });
}
}
Alternatively set "useDefineForClassFields": false in tsconfig.json, but fixing the classes is cleaner.
Other behaviour changes
- Inert elements: actions such as
click(),check(),fill()andselectOption()wait for the element to not be inert. Clicking inside aninertcontainer now times out. - Empty shards: a sharded run fails with “No tests found” when no tests match across all shards. Use
--pass-with-no-teststo allow it. - Cookies follow RFC 6265: host-only cookies are no longer returned for subdomains, and
Path=/apicookies no longer match/apiv2. - Proxy credentials are no longer sent to websites. Use
httpCredentialsfor site authentication. - Accessible names no longer include text the browser does not render, such as content inside a closed
<details>. This can changegetByRole()results and ARIA snapshots. - Query params: string and
URLSearchParamsvalues of theparamsoption are now appended to the URL’s existing query instead of replacing it. - Style imports:
.css,.lessand.scssimports in test files load as empty modules, so the file must exist.
A safe upgrade plan
- Move CI to Node.js 22 or newer if it still uses Node.js 20.
- Upgrade on a branch and run the full suite once.
- Search page objects for field initialisers that use
this.page. - Look for new timeouts around modals and drawers that use
inert. - Re-record ARIA snapshots only after confirming the change is the new accessible-name rule.
- Switch CI to the
failuresreporter, then try a Bun job if you are curious.
Takeaway
Playwright 1.65 gives you cleaner CI output, a proper way to isolate dangerous tests and an early Bun path. The class-field and inert changes are the ones most likely to break an existing suite, so budget a short upgrade branch rather than a blind version bump.
