diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index 6f47843edf..b9804c6eb9 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -280,7 +280,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/run-test with: - browsers-to-install: chromium-headless-shell + browsers-to-install: chromium chromium-headless-shell command: npm run ctest bot-name: "headless-shell-${{ matrix.runs-on }}" flakiness-client-id: ${{ secrets.AZURE_FLAKINESS_DASHBOARD_CLIENT_ID }} diff --git a/docs/src/browsers.md b/docs/src/browsers.md index fc3dfb38a1..52b3272096 100644 --- a/docs/src/browsers.md +++ b/docs/src/browsers.md @@ -472,6 +472,12 @@ rarely the case), you will also want to use the official channel. Google Chrome and Microsoft Edge respect enterprise policies, which include limitations to the capabilities, network proxy, mandatory extensions that stand in the way of testing. So if you are part of the organization that uses such policies, it is easiest to use bundled Chromium for your local testing, you can still opt into stable channels on the bots that are typically free of such restrictions. +### Chromium Headless Shell + +Playwright runs a regular Chromium build in headed and headless modes. Note that headless mode has changed in Playwright version 1.49 when Chromium entirely switched to the [new headless implementation](https://developer.chrome.com/docs/chromium/headless). + +Playwright also provides [`'chromium-headless-shell'` channel](https://developer.chrome.com/blog/chrome-headless-shell) that differs from the regular Chromium browser in features, performance and behavior. If you would like to optimize your CI performance and can tolerate different behavior in some cases, install and use this channel similarly to [Google Chrome & Microsoft Edge](#google-chrome--microsoft-edge). + ### Firefox Playwright's Firefox version matches the recent [Firefox Stable](https://www.mozilla.org/en-US/firefox/new/) build. Playwright doesn't work with the branded version of Firefox since it relies on patches. diff --git a/docs/src/release-notes-js.md b/docs/src/release-notes-js.md index f6bd430163..6919b661a3 100644 --- a/docs/src/release-notes-js.md +++ b/docs/src/release-notes-js.md @@ -6,6 +6,68 @@ toc_max_heading_level: 2 import LiteYouTube from '@site/src/components/LiteYouTube'; +## Version 1.49 + +### New Chromium Headless + +Prior to this release, Playwright was running the old established implementation of Chromium headless. However, Chromium had entirely switched to the [new headless mode](https://developer.chrome.com/docs/chromium/headless) implementation, so Playwright had to switch as well. + +Most likely, this change should go unnoticed for you. However, the new headless implementation differs in a number of ways, for example when handling pdf documents, so please file an issue if you encounter a problem. + +### Chromium Headless Shell + +Playwright now also ships `chromium-headless-shell` channel that is a separate build that closely follows the old headless implementation. If you would like to keep the old behavior before you are ready to switch to the new headless, please fallback to this channel: + +1. First, install this channel prior to running tests. Make sure to list all browsers that you use. + + ```bash + # running tests in all three browsers, headless and headed + npx playwright install chromium chromium-headless-shell firefox webkit + + # running tests in all three browsers on CI, headless only + npx playwright install chromium-headless-shell firefox webkit + ``` + +1. Update your config file to specify `'chromium-headless-shell'` channel. + + ```js + import { defineConfig, devices } from '@playwright/test'; + + export default defineConfig({ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + channel: 'chromium-headless-shell', + }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + ], + }); + ``` + +1. Note that `chromium-headless-shell` channel only supports headless operations. If you try to run tests in headed mode, it will automatically fallback to regular `chromium`. + +### Browser Versions + +- Chromium 131.0.6778.24 +- Mozilla Firefox 132.0 +- WebKit 18.0 + +This version was also tested against the following stable channels: + +- Google Chrome 130 +- Microsoft Edge 130 + + ## Version 1.48 { } + getExecutableName(options: types.LaunchOptions): string { + return options.channel || this._name; + } + abstract defaultArgs(options: types.LaunchOptions, isPersistent: boolean, userDataDir: string): string[]; abstract connectToTransport(transport: ConnectionTransport, options: BrowserOptions): Promise; abstract amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env; diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 2bae0eaf5a..07ce3211fd 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -294,8 +294,6 @@ export class Chromium extends BrowserType { throw new Error('Playwright manages remote debugging connection itself.'); if (args.find(arg => !arg.startsWith('-'))) throw new Error('Arguments can not specify page to be opened'); - if (!options.headless && options.channel === 'chromium-headless-shell') - throw new Error('Cannot launch headed Chromium with `chromium-headless-shell` channel. Consider using regular Chromium instead.'); const chromeArguments = [...chromiumSwitches]; if (os.platform() === 'darwin') { @@ -349,6 +347,12 @@ export class Chromium extends BrowserType { return new ChromiumReadyState(); return undefined; } + + override getExecutableName(options: types.LaunchOptions): string { + if (options.channel === 'chromium-headless-shell' && !options.headless) + return 'chromium'; + return options.channel || 'chromium'; + } } class ChromiumReadyState extends BrowserReadyState { diff --git a/tests/library/browsertype-connect.spec.ts b/tests/library/browsertype-connect.spec.ts index cd77a210d5..085c75c521 100644 --- a/tests/library/browsertype-connect.spec.ts +++ b/tests/library/browsertype-connect.spec.ts @@ -170,7 +170,6 @@ for (const kind of ['launchServer', 'run-server'] as const) { }); test('should ignore page.pause when headed', async ({ connect, startRemoteServer, browserType, channel }) => { - test.skip(channel === 'chromium-headless-shell', 'Headless Shell does not support headed mode'); const headless = (browserType as any)._defaultLaunchOptions.headless; (browserType as any)._defaultLaunchOptions.headless = false; const remoteServer = await startRemoteServer(kind); diff --git a/tests/library/chromium/launcher.spec.ts b/tests/library/chromium/launcher.spec.ts index d891d99481..09e6673642 100644 --- a/tests/library/chromium/launcher.spec.ts +++ b/tests/library/chromium/launcher.spec.ts @@ -185,9 +185,12 @@ it('should not create pages automatically', async ({ browserType }) => { expect(targets.length).toBe(0); }); -it('should throw helpful error when launching chromium-headless-shell channel as headed', async ({ browserType, channel }) => { +it('should fallback to regular chromium when running chromium-headless-shell channel as headed', async ({ browserType, channel }) => { it.skip(channel !== 'chromium-headless-shell'); - await expect(browserType.launch({ channel: 'chromium-headless-shell', headless: false })).rejects.toThrow( - 'Cannot launch headed Chromium with `chromium-headless-shell` channel. Consider using regular Chromium instead.' - ); + + const browser = await browserType.launch({ channel: 'chromium-headless-shell', headless: false }); + const page = await browser.newPage(); + const ua = await page.evaluate(() => navigator.userAgent); + await browser.close(); + expect(ua).not.toContain('Headless'); }); diff --git a/tests/library/headful.spec.ts b/tests/library/headful.spec.ts index 92f2e044e0..5832ef44da 100644 --- a/tests/library/headful.spec.ts +++ b/tests/library/headful.spec.ts @@ -19,7 +19,6 @@ import { PNG } from 'playwright-core/lib/utilsBundle'; import { expect, playwrightTest as it } from '../config/browserTest'; it.use({ headless: false }); -it.skip(({ channel }) => channel === 'chromium-headless-shell'); it('should have default url when launching browser @smoke', async ({ launchPersistent }) => { const { context } = await launchPersistent(); diff --git a/tests/library/launcher.spec.ts b/tests/library/launcher.spec.ts index e7d71182cb..4104c9e747 100644 --- a/tests/library/launcher.spec.ts +++ b/tests/library/launcher.spec.ts @@ -44,7 +44,6 @@ it('should kill browser process on timeout after close', async ({ browserType, m it('should throw a friendly error if its headed and there is no xserver on linux running', async ({ mode, browserType, platform, channel }) => { it.skip(platform !== 'linux'); it.skip(mode.startsWith('service')); - it.skip(channel === 'chromium-headless-shell', 'Headless Shell is always headless'); const error: Error = await browserType.launch({ headless: false,