feat(launcher): allow options to be passed into puppeteer.defaultArgs (#2950)
This commit is contained in:
parent
2e0007669d
commit
6825088644
17
docs/api.md
17
docs/api.md
@ -16,7 +16,7 @@ Next Release: **Aug 9, 2018**
|
||||
- [class: Puppeteer](#class-puppeteer)
|
||||
* [puppeteer.connect(options)](#puppeteerconnectoptions)
|
||||
* [puppeteer.createBrowserFetcher([options])](#puppeteercreatebrowserfetcheroptions)
|
||||
* [puppeteer.defaultArgs()](#puppeteerdefaultargs)
|
||||
* [puppeteer.defaultArgs([options])](#puppeteerdefaultargsoptions)
|
||||
* [puppeteer.executablePath()](#puppeteerexecutablepath)
|
||||
* [puppeteer.launch([options])](#puppeteerlaunchoptions)
|
||||
- [class: BrowserFetcher](#class-browserfetcher)
|
||||
@ -378,9 +378,20 @@ This methods attaches Puppeteer to an existing Chromium instance.
|
||||
- `platform` <[string]> Possible values are: `mac`, `win32`, `win64`, `linux`. Defaults to the current platform.
|
||||
- returns: <[BrowserFetcher]>
|
||||
|
||||
#### puppeteer.defaultArgs()
|
||||
#### puppeteer.defaultArgs([options])
|
||||
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
|
||||
- `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`.
|
||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||
- `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
|
||||
- `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||
- returns: <[Array]<[string]>> The default flags that Chromium will be launched with.
|
||||
|
||||
`puppeteer.defaultArgs` can be used to turn off some flags that Puppeteer usually launches Chromium with:
|
||||
```js
|
||||
const customArgs = puppeteer.defaultArgs().filter(arg => arg !== '--mute-audio');
|
||||
const browser = await puppeteer.launch({ignoreDefaultArgs: true, args: customArgs});
|
||||
```
|
||||
|
||||
#### puppeteer.executablePath()
|
||||
- returns: <[string]> A path where Puppeteer expects to find bundled Chromium. Chromium might not exist there if the download was skipped with [`PUPPETEER_SKIP_CHROMIUM_DOWNLOAD`](#environment-variables).
|
||||
|
||||
@ -398,7 +409,7 @@ This methods attaches Puppeteer to an existing Chromium instance.
|
||||
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
||||
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||
- `ignoreDefaultArgs` <[boolean]> Do not use [`puppeteer.defaultArgs()`](#puppeteerdefaultargs). Dangerous option; use with care. Defaults to `false`.
|
||||
- `ignoreDefaultArgs` <[boolean]> Do not use [`puppeteer.defaultArgs()`](#puppeteerdefaultargs-options). Dangerous option; use with care. Defaults to `false`.
|
||||
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
||||
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
||||
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
||||
|
@ -297,9 +297,3 @@ helper.tracePublicAPI(BrowserContext);
|
||||
helper.tracePublicAPI(Browser);
|
||||
|
||||
module.exports = {Browser, BrowserContext};
|
||||
|
||||
/**
|
||||
* @typedef {Object} BrowserOptions
|
||||
* @property {boolean=} setDefaultViewport
|
||||
* @property {boolean=} ignoreHTTPSErrors
|
||||
*/
|
||||
|
120
lib/Launcher.js
120
lib/Launcher.js
@ -48,9 +48,6 @@ const DEFAULT_ARGS = [
|
||||
'--metrics-recording-only',
|
||||
'--no-first-run',
|
||||
'--safebrowsing-disable-auto-update',
|
||||
];
|
||||
|
||||
const AUTOMATION_ARGS = [
|
||||
'--enable-automation',
|
||||
'--password-store=basic',
|
||||
'--use-mock-keychain',
|
||||
@ -58,52 +55,43 @@ const AUTOMATION_ARGS = [
|
||||
|
||||
class Launcher {
|
||||
/**
|
||||
* @param {!(LaunchOptions & BrowserOptions)=} options
|
||||
* @param {!(LaunchOptions & ChromeArgOptions & BrowserOptions)=} options
|
||||
* @return {!Promise<!Browser>}
|
||||
*/
|
||||
static async launch(options) {
|
||||
options = Object.assign({}, options || {});
|
||||
static async launch(options = {}) {
|
||||
const {
|
||||
ignoreDefaultArgs = false,
|
||||
args = [],
|
||||
dumpio = false,
|
||||
executablePath = null,
|
||||
pipe = false,
|
||||
env = process.env,
|
||||
handleSIGINT = true,
|
||||
handleSIGTERM = true,
|
||||
handleSIGHUP = true,
|
||||
ignoreHTTPSErrors = false,
|
||||
defaultViewport = {width: 800, height: 600},
|
||||
slowMo = 0,
|
||||
timeout = 30000
|
||||
} = options;
|
||||
|
||||
const chromeArguments = !ignoreDefaultArgs ? this.defaultArgs(options) : args;
|
||||
let temporaryUserDataDir = null;
|
||||
const chromeArguments = [];
|
||||
if (!options.ignoreDefaultArgs)
|
||||
chromeArguments.push(...DEFAULT_ARGS);
|
||||
if (!options.ignoreDefaultArgs)
|
||||
chromeArguments.push(...AUTOMATION_ARGS);
|
||||
|
||||
if (!options.ignoreDefaultArgs || !chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
|
||||
chromeArguments.push(options.pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0');
|
||||
|
||||
if (!options.args || !options.args.some(arg => arg.startsWith('--user-data-dir'))) {
|
||||
if (!options.userDataDir)
|
||||
temporaryUserDataDir = await mkdtempAsync(CHROME_PROFILE_PATH);
|
||||
|
||||
chromeArguments.push(`--user-data-dir=${options.userDataDir || temporaryUserDataDir}`);
|
||||
if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
|
||||
chromeArguments.push(pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0');
|
||||
if (!chromeArguments.some(arg => arg.startsWith('--user-data-dir'))) {
|
||||
temporaryUserDataDir = await mkdtempAsync(CHROME_PROFILE_PATH);
|
||||
chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
|
||||
}
|
||||
if (options.devtools === true) {
|
||||
chromeArguments.push('--auto-open-devtools-for-tabs');
|
||||
options.headless = false;
|
||||
}
|
||||
if (typeof options.headless !== 'boolean' || options.headless) {
|
||||
chromeArguments.push(
|
||||
'--headless',
|
||||
'--hide-scrollbars',
|
||||
'--mute-audio'
|
||||
);
|
||||
if (os.platform() === 'win32')
|
||||
chromeArguments.push('--disable-gpu');
|
||||
}
|
||||
if (Array.isArray(options.args) && options.args.every(arg => arg.startsWith('-')))
|
||||
chromeArguments.push('about:blank');
|
||||
|
||||
let chromeExecutable = options.executablePath;
|
||||
if (typeof chromeExecutable !== 'string') {
|
||||
let chromeExecutable = executablePath;
|
||||
if (!executablePath) {
|
||||
const browserFetcher = new BrowserFetcher();
|
||||
const revisionInfo = browserFetcher.revisionInfo(ChromiumRevision);
|
||||
assert(revisionInfo.local, `Chromium revision is not downloaded. Run "npm install" or "yarn install"`);
|
||||
chromeExecutable = revisionInfo.executablePath;
|
||||
}
|
||||
if (Array.isArray(options.args))
|
||||
chromeArguments.push(...options.args);
|
||||
|
||||
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
||||
const stdio = usePipe ? ['ignore', 'ignore', 'ignore', 'pipe', 'pipe'] : ['pipe', 'pipe', 'pipe'];
|
||||
@ -115,12 +103,12 @@ class Launcher {
|
||||
// process group, making it possible to kill child process tree with `.kill(-pid)` command.
|
||||
// @see https://nodejs.org/api/child_process.html#child_process_options_detached
|
||||
detached: process.platform !== 'win32',
|
||||
env: options.env || process.env,
|
||||
env,
|
||||
stdio
|
||||
}
|
||||
);
|
||||
|
||||
if (options.dumpio) {
|
||||
if (dumpio) {
|
||||
chromeProcess.stderr.pipe(process.stderr);
|
||||
chromeProcess.stdout.pipe(process.stdout);
|
||||
}
|
||||
@ -141,22 +129,16 @@ class Launcher {
|
||||
});
|
||||
|
||||
const listeners = [ helper.addEventListener(process, 'exit', killChrome) ];
|
||||
if (options.handleSIGINT !== false)
|
||||
if (handleSIGINT)
|
||||
listeners.push(helper.addEventListener(process, 'SIGINT', () => { killChrome(); process.exit(130); }));
|
||||
if (options.handleSIGTERM !== false)
|
||||
if (handleSIGTERM)
|
||||
listeners.push(helper.addEventListener(process, 'SIGTERM', gracefullyCloseChrome));
|
||||
if (options.handleSIGHUP !== false)
|
||||
if (handleSIGHUP)
|
||||
listeners.push(helper.addEventListener(process, 'SIGHUP', gracefullyCloseChrome));
|
||||
/** @type {?Connection} */
|
||||
let connection = null;
|
||||
try {
|
||||
const {
|
||||
ignoreHTTPSErrors = false,
|
||||
defaultViewport = {width: 800, height: 600},
|
||||
slowMo = 0
|
||||
} = options;
|
||||
if (!usePipe) {
|
||||
const timeout = helper.isNumber(options.timeout) ? options.timeout : 30000;
|
||||
const browserWSEndpoint = await waitForWSEndpoint(chromeProcess, timeout);
|
||||
connection = await Connection.createForWebSocket(browserWSEndpoint, slowMo);
|
||||
} else {
|
||||
@ -228,10 +210,34 @@ class Launcher {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!ChromeArgOptions=} options
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
static defaultArgs() {
|
||||
return DEFAULT_ARGS.concat(AUTOMATION_ARGS);
|
||||
static defaultArgs(options = {}) {
|
||||
const {
|
||||
devtools = false,
|
||||
headless = !devtools,
|
||||
args = [],
|
||||
userDataDir = null
|
||||
} = options;
|
||||
const chromeArguments = [...DEFAULT_ARGS];
|
||||
if (userDataDir)
|
||||
chromeArguments.push(`--user-data-dir=${userDataDir}`);
|
||||
if (devtools)
|
||||
chromeArguments.push('--auto-open-devtools-for-tabs');
|
||||
if (headless) {
|
||||
chromeArguments.push(
|
||||
'--headless',
|
||||
'--hide-scrollbars',
|
||||
'--mute-audio'
|
||||
);
|
||||
if (os.platform() === 'win32')
|
||||
chromeArguments.push('--disable-gpu');
|
||||
}
|
||||
if (args.every(arg => arg.startsWith('-')))
|
||||
chromeArguments.push('about:blank');
|
||||
chromeArguments.push(...args);
|
||||
return chromeArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -317,19 +323,23 @@ function waitForWSEndpoint(chromeProcess, timeout) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} LaunchOptions
|
||||
* @typedef {Object} ChromeArgOptions
|
||||
* @property {boolean=} headless
|
||||
* @property {Array<string>=} args
|
||||
* @property {string=} userDataDir
|
||||
* @property {boolean=} devtools
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} LaunchOptions
|
||||
* @property {string=} executablePath
|
||||
* @property {!Array<string>=} args
|
||||
* @property {boolean=} ignoreDefaultArgs
|
||||
* @property {boolean=} handleSIGINT
|
||||
* @property {boolean=} handleSIGTERM
|
||||
* @property {boolean=} handleSIGHUP
|
||||
* @property {number=} timeout
|
||||
* @property {boolean=} dumpio
|
||||
* @property {string=} userDataDir
|
||||
* @property {!Object<string, string | undefined>=} env
|
||||
* @property {boolean=} devtools
|
||||
* @property {boolean=} pipe
|
||||
*/
|
||||
|
||||
|
@ -44,8 +44,8 @@ module.exports = class {
|
||||
/**
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
static defaultArgs() {
|
||||
return Launcher.defaultArgs();
|
||||
static defaultArgs(options) {
|
||||
return Launcher.defaultArgs(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +60,6 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro
|
||||
await rmAsync(downloadsFolder);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Puppeteer.launch', function() {
|
||||
it('should reject all promises when browser is closed', async() => {
|
||||
const browser = await puppeteer.launch(defaultBrowserOptions);
|
||||
@ -135,8 +134,10 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro
|
||||
await rmAsync(userDataDir).catch(e => {});
|
||||
});
|
||||
it('should return the default chrome arguments', async() => {
|
||||
const args = puppeteer.defaultArgs();
|
||||
expect(args).toContain('--no-first-run');
|
||||
expect(puppeteer.defaultArgs()).toContain('--no-first-run');
|
||||
expect(puppeteer.defaultArgs()).toContain('--headless');
|
||||
expect(puppeteer.defaultArgs({headless: false})).not.toContain('--headless');
|
||||
expect(puppeteer.defaultArgs({userDataDir: 'foo'})).toContain('--user-data-dir=foo');
|
||||
});
|
||||
it('should dump browser process stderr', async({server}) => {
|
||||
const dumpioTextToLog = 'MAGIC_DUMPIO_TEST';
|
||||
@ -183,7 +184,6 @@ module.exports.addTests = function({testRunner, expect, PROJECT_ROOT, defaultBro
|
||||
});
|
||||
it('should support the pipe argument', async() => {
|
||||
const options = Object.assign({}, defaultBrowserOptions);
|
||||
options.ignoreDefaultArgs = true;
|
||||
options.args = ['--remote-debugging-pipe'].concat(options.args);
|
||||
const browser = await puppeteer.launch(options);
|
||||
expect(browser.wsEndpoint()).toBe('');
|
||||
|
Loading…
Reference in New Issue
Block a user