Skip to content

Commit

Permalink
fix(chromium-headless-shell): fallback to chromium when running headed (
Browse files Browse the repository at this point in the history
  • Loading branch information
dgozman authored Nov 8, 2024
1 parent f7a388d commit d561ba7
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests_secondary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
6 changes: 6 additions & 0 deletions docs/src/browsers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
62 changes: 62 additions & 0 deletions docs/src/release-notes-js.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<LiteYouTube
Expand Down
6 changes: 5 additions & 1 deletion packages/playwright-core/src/server/browserType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ export abstract class BrowserType extends SdkObject {
throw new Error(`Failed to launch ${this._name} because executable doesn't exist at ${executablePath}`);
executable = executablePath;
} else {
const registryExecutable = registry.findExecutable(options.channel || this._name);
const registryExecutable = registry.findExecutable(this.getExecutableName(options));
if (!registryExecutable || registryExecutable.browserName !== this._name)
throw new Error(`Unsupported ${this._name} channel "${options.channel}"`);
executable = registryExecutable.executablePathOrDie(this.attribution.playwright.options.sdkLanguage);
Expand Down Expand Up @@ -332,6 +332,10 @@ export abstract class BrowserType extends SdkObject {
async prepareUserDataDir(options: types.LaunchOptions, userDataDir: string): Promise<void> {
}

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<Browser>;
abstract amendEnvironment(env: Env, userDataDir: string, executable: string, browserArguments: string[]): Env;
Expand Down
8 changes: 6 additions & 2 deletions packages/playwright-core/src/server/chromium/chromium.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 0 additions & 1 deletion tests/library/browsertype-connect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 7 additions & 4 deletions tests/library/chromium/launcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
1 change: 0 additions & 1 deletion tests/library/headful.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 0 additions & 1 deletion tests/library/launcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit d561ba7

Please sign in to comment.