Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add channel parameter for puppeteer.launch #7389

Merged
merged 6 commits into from Jul 9, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/api.md
Expand Up @@ -454,7 +454,7 @@ When using `puppeteer-core`, remember to change the _include_ line:
const puppeteer = require('puppeteer-core');
```

You will then need to call [`puppeteer.connect([options])`](#puppeteerconnectoptions) or [`puppeteer.launch([options])`](#puppeteerlaunchoptions) with an explicit `executablePath` option.
You will then need to call [`puppeteer.connect([options])`](#puppeteerconnectoptions) or [`puppeteer.launch([options])`](#puppeteerlaunchoptions) with an explicit `executablePath` or `channel` option.

### Environment Variables

Expand Down Expand Up @@ -627,6 +627,7 @@ try {
- `product` <[string]> Which browser to launch. At this time, this is either `chrome` or `firefox`. See also `PUPPETEER_PRODUCT`.
- `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
- `headless` <[boolean]> Whether to run browser in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`.
- `channel` <[string]> When specified, Puppeteer will search for the locally installed release channel of Google Chrome and use it to launch. Available values are `chrome`, `chrome-beta`, `chrome-canary`, `chrome-dev`. When channel is specified, `executablePath` cannot be specified.
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Puppeteer is only [guaranteed to work](https://github.com/puppeteer/puppeteer/#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
- `slowMo` <[number]> Slows down Puppeteer operations by the specified amount of milliseconds. Useful so that you can see what is going on.
- `defaultViewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport.
Expand Down Expand Up @@ -660,7 +661,7 @@ const browser = await puppeteer.launch({
});
```

> **NOTE** Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use `executablePath` option with extreme caution.
> **NOTE** Puppeteer can also be used to control the Chrome browser, but it works best with the version of Chromium it is bundled with. There is no guarantee it will work with any other version. Use `executablePath` or `channel` option with extreme caution.
>
> If Google Chrome (rather than Chromium) is preferred, a [Chrome Canary](https://www.google.com/chrome/browser/canary.html) or [Dev Channel](https://www.chromium.org/getting-involved/dev-channel) build is suggested.
>
Expand Down
10 changes: 10 additions & 0 deletions src/node/LaunchOptions.ts
Expand Up @@ -46,11 +46,21 @@ export interface BrowserLaunchArgumentOptions {
args?: string[];
}

export type ChromeReleaseChannel =
| 'chrome'
| 'chrome-beta'
| 'chrome-canary'
| 'chrome-dev';

/**
* Generic launch options that can be passed when launching any browser.
* @public
*/
export interface LaunchOptions {
/**
* Chrome Release Channel
*/
channel?: ChromeReleaseChannel;
/**
* Path to a browser executable to use instead of the bundled Chromium. Note
* that Puppeteer is only guaranteed to work with the bundled Chromium, so use
Expand Down
98 changes: 94 additions & 4 deletions src/node/Launcher.ts
Expand Up @@ -17,6 +17,7 @@ import * as os from 'os';
import * as path from 'path';
import * as fs from 'fs';

import { assert } from '../common/assert.js';
import { BrowserFetcher } from './BrowserFetcher.js';
import { Browser } from '../common/Browser.js';
import { BrowserRunner } from './BrowserRunner.js';
Expand All @@ -27,6 +28,7 @@ const writeFileAsync = promisify(fs.writeFile);

import {
BrowserLaunchArgumentOptions,
ChromeReleaseChannel,
PuppeteerNodeLaunchOptions,
} from './LaunchOptions.js';
import { Product } from '../common/Product.js';
Expand All @@ -37,7 +39,7 @@ import { Product } from '../common/Product.js';
*/
export interface ProductLauncher {
launch(object: PuppeteerNodeLaunchOptions);
executablePath: () => string;
executablePath: (string?) => string;
defaultArgs(object: BrowserLaunchArgumentOptions);
product: Product;
}
Expand Down Expand Up @@ -65,6 +67,7 @@ class ChromeLauncher implements ProductLauncher {
ignoreDefaultArgs = false,
args = [],
dumpio = false,
channel = null,
executablePath = null,
pipe = false,
env = process.env,
Expand Down Expand Up @@ -105,7 +108,16 @@ class ChromeLauncher implements ProductLauncher {
}

let chromeExecutable = executablePath;
if (!executablePath) {

if (channel) {
// executablePath is detected by channel, so it should not be specified by user.
assert(
!executablePath,
jschfflr marked this conversation as resolved.
Show resolved Hide resolved
'`executablePath` must not be specified when `channel` is given.'
);

chromeExecutable = executablePathForChannel(channel);
} else if (!executablePath) {
// Use Intel x86 builds on Apple M1 until native macOS arm64
// Chromium builds are available.
if (os.platform() !== 'darwin' && os.arch() === 'arm64') {
Expand Down Expand Up @@ -204,8 +216,12 @@ class ChromeLauncher implements ProductLauncher {
return chromeArguments;
}

executablePath(): string {
return resolveExecutablePath(this).executablePath;
executablePath(channel?: ChromeReleaseChannel): string {
if (channel) {
return executablePathForChannel(channel);
} else {
return resolveExecutablePath(this).executablePath;
}
}

get product(): Product {
Expand Down Expand Up @@ -587,6 +603,80 @@ class FirefoxLauncher implements ProductLauncher {
}
}

function executablePathForChannel(channel: ChromeReleaseChannel): string {
const platform = os.platform();
jschfflr marked this conversation as resolved.
Show resolved Hide resolved

let chromePath: string | undefined;
switch (platform) {
case 'win32':
switch (channel) {
case 'chrome':
chromePath = `${process.env.PROGRAMFILES}\\Google\\Chrome\\Application\\chrome.exe`;
break;
case 'chrome-beta':
chromePath = `${process.env.PROGRAMFILES}\\Google\\Chrome Beta\\Application\\chrome.exe`;
break;
case 'chrome-canary':
chromePath = `${process.env.PROGRAMFILES}\\Google\\Chrome SxS\\Application\\chrome.exe`;
break;
case 'chrome-dev':
chromePath = `${process.env.PROGRAMFILES}\\Google\\Chrome Dev\\Application\\chrome.exe`;
break;
}
break;
case 'darwin':
switch (channel) {
case 'chrome':
chromePath =
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
break;
case 'chrome-beta':
chromePath =
'/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta';
break;
case 'chrome-canary':
chromePath =
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary';
break;
case 'chrome-dev':
chromePath =
'/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev';
break;
}
break;
case 'linux':
switch (channel) {
case 'chrome':
chromePath = '/opt/google/chrome/chrome';
break;
case 'chrome-beta':
chromePath = '/opt/google/chrome-beta/chrome';
break;
case 'chrome-dev':
chromePath = '/opt/google/chrome-unstable/chrome';
break;
}
break;
}

if (!chromePath) {
throw new Error(
`Unable to detect browser executable path for '${channel}' on ${platform}.`
);
}

// Check if Chrome exists and is accessible.
try {
fs.accessSync(chromePath);
} catch (error) {
throw new Error(
`Could not find Google Chrome executable for channel '${channel}' at '${chromePath}'.`
);
}

return chromePath;
}

function resolveExecutablePath(launcher: ChromeLauncher | FirefoxLauncher): {
executablePath: string;
missingText?: string;
Expand Down
4 changes: 2 additions & 2 deletions src/node/Puppeteer.ts
Expand Up @@ -165,8 +165,8 @@ export class PuppeteerNode extends Puppeteer {
* The browser binary might not be there if the download was skipped with
* the `PUPPETEER_SKIP_DOWNLOAD` environment variable.
*/
executablePath(): string {
return this._launcher.executablePath();
executablePath(channel?: string): string {
return this._launcher.executablePath(channel);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions test/launcher.spec.ts
Expand Up @@ -660,6 +660,12 @@ describe('Launcher specs', function () {
expect(fs.existsSync(executablePath)).toBe(true);
expect(fs.realpathSync(executablePath)).toBe(executablePath);
});
it('returns executablePath for channel', () => {
const { puppeteer } = getTestState();

const executablePath = puppeteer.executablePath('chrome');
expect(executablePath).toBeTruthy();
});
});
});

Expand Down