Playwright 1.65 Changes How Page Object Fields Initialize
Playwright 1.65 follows useDefineForClassFields, so page object fields initialized from this.page can throw. It also adds Bun support and failures-only reports.
Playwright 1.65, released November 5, 2026, compiles TypeScript class fields the way tsc does: it now follows useDefineForClassFields from your tsconfig.json. That breaks page objects that build locators in field initializers. A field initializer that reads a constructor parameter property, such as this.page, throws a TypeError as soon as a test builds the page object. The test runner can now also run under Bun, and new reports keep only failures (release notes).
Page object fields that read this.page now throw
The release notes put it plainly: "A field initializer that reads a constructor parameter property now fails, because the initializer runs first." The flag "is enabled for ES2022 and later targets, and when target is not specified", so a project with no target in its tsconfig.json is affected too.
This is the shape that breaks:
class LoginPage {
// TypeError: Cannot read properties of undefined (reading 'getByRole')
readonly submit = this.page.getByRole('button', { name: 'Submit' });
constructor(private readonly page: Page) {}
}The fix is to declare the field and assign it in the constructor:
import { type Locator, type Page } from '@playwright/test';
export class LoginPage {
readonly submit: Locator;
constructor(private readonly page: Page) {
this.submit = page.getByRole('button', { name: 'Submit' });
}
}The other way out, per the notes, is "useDefineForClassFields": false in tsconfig.json. Moving the assignment works under either setting, and tsc already reports the old pattern as error TS2729 ("Property 'page' is used before its initialization") on ES2022 and later targets. To list candidate fields before upgrading, search the test folder:
grep -rnE '^\s+((public|private|protected|readonly|override)\s+)*#?\w+\s*(:[^=]+)?=\s*this\.' testsAn initializer split across lines slips past the search; npx tsc --noEmit --target ES2022 reports those as TS2729 too.
Bun runs the test runner with --bun
The test runner now runs under Bun, which transpiles TypeScript itself. The flag matters: "without it bunx playwright test runs on Node.js".
bunx --bun playwright testThe Playwright team runs its own test runner tests "under Bun 1.4.3 on Linux", and the notes list what differs. There is no watch mode, --tsconfig and custom ESM loaders don't apply, stack traces lose async frames, and console output doesn't reach the trace or UI mode. Step titles change too: "click instead of locator.click". Anything in CI that matches on step titles or error messages needs a check before a team switches runtimes.
Failures-only reports for CI logs
The new failures reporter "prints failures and successful retries as they occur, followed by a summary, without progress output". The HTML, JSON and JUnit reporters get an onlyFailures option that "keeps only tests with unexpected outcomes, flaky tests and interrupted tests":
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [
['failures'],
['html', { onlyFailures: true }],
],
});Flaky tests stay in the trimmed report, so a suite that tracks them, as in fixing flaky Playwright tests, keeps that signal while the report drops the passing rows.
Five more Playwright 1.65 changes to check before upgrading
Upgrade with npm i -D @playwright/test@1.65.0 and npx playwright install --with-deps, then bump a container job to mcr.microsoft.com/playwright:v1.65.0-noble. Run the suite once locally to catch page object errors first. The full list, including the new -exclusive- test lock, is in the Playwright release notes.