Socket
Socket
Sign inDemoInstall

puppeteer

Package Overview
Dependencies
41
Maintainers
3
Versions
878
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.9.0 to 0.10.0

docs/troubleshooting.md

27

.eslintrc.js

@@ -46,2 +46,3 @@ module.exports = {

}],
"brace-style": [2, "1tbs", {"allowSingleLine": true}],
"curly": [2, "multi-or-nest", "consistent"],

@@ -51,2 +52,4 @@ "new-parens": 2,

"arrow-parens": [2, "as-needed"],
"prefer-const": 2,
"quote-props": [2, "consistent"],

@@ -74,2 +77,3 @@ // anti-patterns

"no-unused-vars": [2, { "args": "none", "vars": "local" }],
"no-implicit-globals": [2],

@@ -105,23 +109,8 @@ // es2015 features

"no-trailing-spaces": 2,
"linebreak-style": [ 2, "unix" ],
"linebreak-style": [ process.platform === "win32" ? 0 : 2, "unix" ],
"indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
/**
* Disabled, aspirational rules
*/
// brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions
"brace-style": [0, "allman", { "allowSingleLine": true }],
// key-spacing is disabled, as some objects use value-aligned spacing, some not.
"key-spacing": [0, {
"beforeColon": false,
"afterColon": true,
"align": "value"
}],
// quote-props is diabled, as property quoting styles are too varied to enforce.
"quote-props": [0, "as-needed"],
// no-implicit-globals will prevent accidental globals
"no-implicit-globals": [0]
"key-spacing": [2, {
"beforeColon": false
}]
}
};

@@ -89,3 +89,3 @@ # How to Contribute

fit('should work', SX(async function() {
await response = page.goto(EMPTY_PAGE);
const response = await page.goto(EMPTY_PAGE);
expect(response.ok).toBe(true);

@@ -99,3 +99,3 @@ }))

xit('should work', SX(async function() {
await response = page.goto(EMPTY_PAGE);
const response = await page.goto(EMPTY_PAGE);
expect(response.ok).toBe(true);

@@ -140,9 +140,7 @@ }))

(async() => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
browser.close();
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
browser.close();
})();

@@ -149,0 +147,0 @@ ```

@@ -631,3 +631,3 @@ /**

];
for (let device of module.exports)
for (const device of module.exports)
module.exports[device.name] = device;

@@ -1,3 +0,5 @@

# Puppeteer API v<!-- GEN:version -->0.9.0<!-- GEN:stop-->
##### Released API: [v0.9.0](https://github.com/GoogleChrome/puppeteer/blob/v0.9.0/docs/api.md)
# Puppeteer API v<!-- GEN:version -->0.10.0<!-- GEN:stop-->
##### Table of Contents

@@ -30,6 +32,11 @@

+ [page.$(selector)](#pageselector)
+ [page.$$(selector)](#pageselector)
+ [page.addScriptTag(url)](#pageaddscripttagurl)
+ [page.click(selector[, options])](#pageclickselector-options)
+ [page.close()](#pageclose)
+ [page.content()](#pagecontent)
+ [page.cookies(...urls)](#pagecookiesurls)
+ [page.deleteCookie(...cookies)](#pagedeletecookiecookies)
+ [page.emulate(options)](#pageemulateoptions)
+ [page.emulateMedia(mediaType)](#pageemulatemediamediatype)
+ [page.evaluate(pageFunction, ...args)](#pageevaluatepagefunction-args)

@@ -54,3 +61,5 @@ + [page.evaluateOnNewDocument(pageFunction, ...args)](#pageevaluateonnewdocumentpagefunction-args)

+ [page.setContent(html)](#pagesetcontenthtml)
+ [page.setCookie(...cookies)](#pagesetcookiecookies)
+ [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
+ [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled)
+ [page.setRequestInterceptionEnabled(value)](#pagesetrequestinterceptionenabledvalue)

@@ -62,3 +71,2 @@ + [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)

+ [page.type(text, options)](#pagetypetext-options)
+ [page.uploadFile(selector, ...filePaths)](#pageuploadfileselector-filepaths)
+ [page.url()](#pageurl)

@@ -90,2 +98,3 @@ + [page.viewport()](#pageviewport)

+ [frame.$(selector)](#frameselector)
+ [frame.$$(selector)](#frameselector)
+ [frame.addScriptTag(url)](#frameaddscripttagurl)

@@ -99,3 +108,2 @@ + [frame.childFrames()](#framechildframes)

+ [frame.title()](#frametitle)
+ [frame.uploadFile(selector, ...filePaths)](#frameuploadfileselector-filepaths)
+ [frame.url()](#frameurl)

@@ -110,2 +118,3 @@ + [frame.waitFor(selectorOrFunctionOrTimeout[, options])](#framewaitforselectororfunctionortimeout-options)

+ [elementHandle.hover()](#elementhandlehover)
+ [elementHandle.uploadFile(...filePaths)](#elementhandleuploadfilefilepaths)
* [class: Request](#class-request)

@@ -142,4 +151,5 @@ + [request.abort()](#requestabort)

const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.goto('https://www.google.com');

@@ -166,2 +176,4 @@ // other actions...

- `args` <[Array]<[string]>> Additional arguments to pass to the Chromium instance. List of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
- `handleSIGINT` <[boolean]> Close chrome process on Ctrl-C. Defaults to `true`.
- `timeout` <[number]> Maximum time in milliseconds to wait for the Chrome instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
- `dumpio` <[boolean]> Whether to pipe browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.

@@ -172,3 +184,3 @@ - returns: <[Promise]<[Browser]>> Promise which resolves to browser instance.

> **Note** Puppeteer 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 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. 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.

@@ -184,3 +196,3 @@ ### class: Browser

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.goto('https://example.com');

@@ -207,3 +219,3 @@ browser.close();

Browser websocket endpoint which could be used as an argument to
[puppeteer.connect](#puppeteerconnect).
[puppeteer.connect](#puppeteerconnectoptions).

@@ -219,3 +231,3 @@ ### class: Page

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.goto('https://example.com');

@@ -237,3 +249,3 @@ await page.screenshot({path: 'screenshot.png'});

page.on('console', (...args) => {
for (let i =0; i < args.length; ++i)
for (let i = 0; i < args.length; ++i)
console.log(`${i}: ${args[i]}`);

@@ -254,3 +266,3 @@ });

> **Note** `error` event has a special meaning in Node, see [error events](https://nodejs.org/api/events.html#events_error_events) for details.
> **NOTE** `error` event has a special meaning in Node, see [error events](https://nodejs.org/api/events.html#events_error_events) for details.

@@ -274,3 +286,3 @@ #### event: 'frameattached'

Emitted when the JavaScriot [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.
Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.

@@ -311,2 +323,10 @@ #### event: 'pageerror'

#### page.$$(selector)
- `selector` <[string]> Selector to query page for
- returns: <[Promise]<[Array]<[ElementHandle]>>>
The method runs `document.querySelectorAll` within the page. If no elements match the selector, the return value resolve to `[]`.
Shortcut for [page.mainFrame().$$(selector)](#frameselector-1).
#### page.addScriptTag(url)

@@ -329,3 +349,4 @@ - `url` <[string]> Url of the `<script>` tag

Shortcut for [page.mainFrame().click(selector[, options])](#frameclickselector-options).
This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.mouse](#pagemouse) to click in the center of the element.
If there's no element matching `selector`, the method throws an error.

@@ -335,2 +356,31 @@ #### page.close()

#### page.content()
- returns: <[Promise]<[String]>>
Gets the full HTML contents of the page, including the doctype.
#### page.cookies(...urls)
- `...urls` <...[string]>
- returns: <[Promise]<[Array]<[Object]>>>
- `name` <[string]>
- `value` <[string]>
- `domain` <[string]>
- `path` <[string]>
- `expires` <[number]> Unix time in seconds.
- `httpOnly` <[boolean]>
- `secure` <[boolean]>
- `sameSite` <[string]> `"Strict"` or `"Lax"`.
If no URLs are specified, this method returns cookies for the current page URL.
If URLs are specified, only cookies for those URLs are returned.
#### page.deleteCookie(...cookies)
- `...cookies` <...[Object]>
- `name` <[string]> **required**
- `url` <[string]>
- `domain` <[string]>
- `path` <[string]>
- `secure` <[boolean]>
- returns: <[Promise]>
#### page.emulate(options)

@@ -341,3 +391,3 @@ - `options` <[Object]>

- `height` <[number]> page height in pixels.
- `deviceScaleFactor` <[number]> Specify device scale factor (could be though of as dpr). Defaults to `1`.
- `deviceScaleFactor` <[number]> Specify device scale factor (could be thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.

@@ -361,3 +411,3 @@ - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.emulate(iPhone);

@@ -372,6 +422,10 @@ await page.goto('https://www.google.com');

#### page.emulateMedia(mediaType)
- `mediaType` <[string]> Changes the CSS media type of the page. The only allowed values are `'screen'`, `'media'` and `null`. Passing `null` disables media emulation.
- returns: <[Promise]>
#### page.evaluate(pageFunction, ...args)
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
- `...args` Arguments to pass to `pageFunction`
- returns: <[Promise]> Resolves to the return value of `pageFunction`
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Serializable]>> Resolves to the return value of `pageFunction`

@@ -382,4 +436,5 @@ If the function, passed to the `page.evaluate`, returns a [Promise], then `page.evaluate` would wait for the promise to resolve and return it's value.

const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
const result = await page.evaluate(() => {

@@ -403,3 +458,3 @@ return Promise.resolve(8 * 7);

- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
- `...args` Arguments to pass to `pageFunction`
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
- returns: <[Promise]>

@@ -411,3 +466,3 @@

The function is invoked after the document was created but before any of its scripts were run. This is useful to amend JavaScript environment, e.g. to seed [Math.random](https://github.com/GoogleChrome/puppeteer/blob/master/examples/unrandomize.js)
The function is invoked after the document was created but before any of its scripts were run. This is useful to amend JavaScript environment, e.g. to seed `Math.random`.

@@ -417,3 +472,3 @@ #### page.exposeFunction(name, puppeteerFunction)

- `puppeteerFunction` <[function]> Callback function which will be called in Puppeteer's context.
- returns: <[Promise]> Promise which resolves with the result of `puppeteerFunction`.
- returns: <[Promise]>

@@ -433,9 +488,11 @@ The method adds a function called `name` on the page's `window` object.

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
page.on('console', console.log);
await page.exposeFunction('md5', text => crypto.createHash('md5').update(text).digest('hex'));
await page.exposeFunction('md5', text =>
crypto.createHash('md5').update(text).digest('hex')
);
await page.evaluate(async () => {
// use window.md5 to compute hashes
let myString = 'PUPPETEER';
let myHash = await window.md5(myString);
const myString = 'PUPPETEER';
const myHash = await window.md5(myString);
console.log(`md5 of ${myString} is ${myHash}`);

@@ -454,3 +511,3 @@ });

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
page.on('console', console.log);

@@ -469,3 +526,3 @@ await page.exposeFunction('readfile', async filePath => {

// use window.readfile to read contents of a file
let content = await window.readfile('/etc/hosts');
const content = await window.readfile('/etc/hosts');
console.log(content);

@@ -482,3 +539,4 @@ });

Shortcut for [page.mainFrame().focus(selector)](#framefocusselector).
This method fetches an element with `selector` and focuses it.
If there's no element matching `selector`, the method throws an error.

@@ -515,3 +573,3 @@ #### page.frames()

#### page.goto(url, options)
- `url` <[string]> URL to navigate page to
- `url` <[string]> URL to navigate page to. The url should include scheme, e.g. `https://`.
- `options` <[Object]> Navigation parameters which might have the following properties:

@@ -539,3 +597,4 @@ - `timeout` <[number]> Maximum navigation time in milliseconds, defaults to 30 seconds.

Shortcut for [page.mainFrame().hover(selector)](#framehoverselector).
This method fetches an element with `selector`, scrolls it into view if needed, and then uses [page.mouse](#pagemouse) to hover over the center of the element.
If there's no element matching `selector`, the method throws an error.

@@ -563,3 +622,3 @@ #### page.injectFile(filePath)

- `options` <[Object]> Options object which might have the following properties:
- `path` <[string]> The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- `path` <[string]> The file path to save the PDF to. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). If no path is provided, the PDF won't be saved to the disk.
- `scale` <[number]> Scale of the webpage rendering. Defaults to `1`.

@@ -580,2 +639,10 @@ - `displayHeaderFooter` <[boolean]> Display header and footer. Defaults to `false`.

`page.pdf()` generates a pdf of the page with `print` css media. To generate a pdf with `screen` media, call [page.emulateMedia('screen')](#pageemulatemediamediatype) before calling `page.pdf()`:
```js
// Generates a PDF with 'screen' media type.
await page.emulateMedia('screen');
await page.pdf({path: 'page.pdf'});
```
The `width`, `height`, and `margin` options accept values labeled with units. Unlabeled values are treated as pixels.

@@ -632,3 +699,3 @@

- `options` <[Object]> Options object which might have the following properties:
- `path` <[string]> The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- `path` <[string]> The file path to save the image to. The screenshot type will be inferred from file extension. If `path` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). If no path is provided, the image won't be saved to the disk.
- `type` <[string]> Specify screenshot type, could be either `jpeg` or `png`. Defaults to 'png'.

@@ -642,2 +709,3 @@ - `quality` <[number]> The quality of the image, between 0-100. Not applicable to `png` images.

- `height` <[number]> height of clipping area
- `omitBackground` <[boolean]> Hides default white background and allows capturing screenshots with transparency. Defaults to `false`.
- returns: <[Promise]<[Buffer]>> Promise which resolves to buffer with captured screenshot

@@ -649,2 +717,15 @@

#### page.setCookie(...cookies)
- `...cookies` <...[Object]>
- `name` <[string]> **required**
- `value` <[string]> **required**
- `url` <[string]>
- `domain` <[string]>
- `path` <[string]>
- `expires` <[number]> Unix time in seconds.
- `httpOnly` <[boolean]>
- `secure` <[boolean]>
- `sameSite` <[string]> `"Strict"` or `"Lax"`.
- returns: <[Promise]>
#### page.setExtraHTTPHeaders(headers)

@@ -658,3 +739,8 @@ - `headers` <[Map]> A map of additional http headers to be sent with every request.

#### page.setJavaScriptEnabled(enabled)
- `enabled` <[boolean]> Whether or not to enable JavaScript on the page.
- returns: <[Promise]>
> **NOTE** changing this value won't affect scripts that have already been run. It will take full effect on the next [navigation](#pagegotourl-options).
#### page.setRequestInterceptionEnabled(value)

@@ -669,6 +755,7 @@ - `value` <[boolean]> Whether to enable request interception.

const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.setRequestInterceptionEnabled(true);
page.on('request', request => {
page.on('request', interceptedRequest => {
if (interceptedRequest.url.endsWith('.png') || interceptedRequest.url.endsWith('.jpg'))

@@ -692,3 +779,3 @@ interceptedRequest.abort();

- `height` <[number]> page height in pixels.
- `deviceScaleFactor` <[number]> Specify device scale factor (could be though of as dpr). Defaults to `1`.
- `deviceScaleFactor` <[number]> Specify device scale factor (could be thought of as dpr). Defaults to `1`.
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.

@@ -726,9 +813,2 @@ - `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`

#### page.uploadFile(selector, ...filePaths)
- `selector` <[string]> A [selector] to a file input
- `...filePaths` <[string]> Sets the value of the file input these paths. If some of the `filePaths` are relative paths, then they are resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- returns: <[Promise]> Promise which resolves when the value is set.
Shortcut for [page.mainFrame().uploadFile(selector, ...filePaths)](#frameuploadfileselector-filepaths).
#### page.url()

@@ -754,4 +834,4 @@ - returns: <[string]>

This method behaves differently with respect to the type of the first parameter:
- if `selectorOrFunctionOrTimeout` is a `string`, than the first argument is treated as a [selector] to wait for and the method is a shortcut for [frame.waitForSelector](#framewaitforselectorselector-options)
- if `selectorOrFunctionOrTimeout` is a `function`, than the first argument is treated as a predicate to wait for and the method is a shortcut for [frame.waitForFunction()](#framewaitforfunctionpagefunction-options-args).
- if `selectorOrFunctionOrTimeout` is a `string`, than the first argument is treated as a [selector] to wait for and the method is a shortcut for [page.waitForSelector](#pagewaitforselectorselector-options)
- if `selectorOrFunctionOrTimeout` is a `function`, than the first argument is treated as a predicate to wait for and the method is a shortcut for [page.waitForFunction()](#pagewaitforfunctionpagefunction-options-args).
- if `selectorOrFunctionOrTimeout` is a `number`, than the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout

@@ -769,4 +849,4 @@ - otherwise, an exception is thrown

- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds).
- `...args` <...[Object]> Arguments to pass to `pageFunction`
- returns: <[Promise]> Promise which resolves when element specified by selector string is added to DOM.
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
- returns: <[Promise]> Promise which resolves when the `pageFunction` returns a truthy value.

@@ -776,4 +856,5 @@ The `waitForFunction` could be used to observe viewport size change:

const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
const watchDog = page.waitForFunction('window.innerWidth < 100');

@@ -811,6 +892,9 @@ page.setViewport({width: 50, height: 50});

const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
let currentURL;
page.waitForSelector('img').then(() => console.log('First URL with image: ' + currentURL));
page
.waitForSelector('img')
.then(() => console.log('First URL with image: ' + currentURL));
for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])

@@ -825,3 +909,3 @@ await page.goto(currentURL);

Keyboard provides an api for managing a virtual keyboard. The high level api is [`page.type`](#pageypetext), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.
Keyboard provides an api for managing a virtual keyboard. The high level api is [`page.type`](#pagetypetext-options), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.

@@ -885,3 +969,3 @@ For finer control, you can use [`keyboard.down`](#keyboarddownkey-options), [`keyboard.up`](#keyboardupkey), and [`keyboard.sendCharacter`](#keyboardsendcharacterchar) to manually fire events as if they were generated from a real keyboard.

Shortcut for [`mouse.move`](#mousemovexy), [`mouse.down`](#mousedownkey) and [`mouse.up`](#mouseupkey).
Shortcut for [`mouse.move`](#mousemovex-y), [`mouse.down`](#mousedownoptions) and [`mouse.up`](#mouseupoptions).

@@ -913,3 +997,3 @@ #### mouse.down([options])

You can use [`tracing.start`](#tracingstartoptions) and [`tracing.stop`](#tracingstoppath) to create a trace file which can be opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/).
You can use [`tracing.start`](#tracingstartoptions) and [`tracing.stop`](#tracingstop) to create a trace file which can be opened in Chrome DevTools or [timeline viewer](https://chromedevtools.github.io/timeline-viewer/).

@@ -942,6 +1026,6 @@ ```js

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
page.on('dialog', dialog => {
const page = await browser.newPage();
page.on('dialog', async dialog => {
console.log(dialog.message());
dialog.dismiss();
await dialog.dismiss();
browser.close();

@@ -955,3 +1039,3 @@ });

- `promptText` <[string]> A text to enter in prompt. Does not cause any effects if the dialog's `type` is not prompt.
- returns: <[Promise]> Promise which resolves when the dialog has being accepted.
- returns: <[Promise]> Promise which resolves when the dialog has been accepted.

@@ -962,3 +1046,3 @@ #### dialog.defaultValue()

#### dialog.dismiss()
- returns: <[Promise]> Promise which resolves when the dialog has being dismissed.
- returns: <[Promise]> Promise which resolves when the dialog has been dismissed.

@@ -988,3 +1072,3 @@ #### dialog.message()

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.goto('https://www.google.com/chrome/browser/canary.html');

@@ -1004,7 +1088,12 @@ dumpFrameTree(page.mainFrame(), '');

- `selector` <[string]> Selector to query page for
- returns: <[Promise]<[ElementHandle]>> Promise which resolves to ElementHandle pointing to the page element.
- returns: <[Promise]<[ElementHandle]>> Promise which resolves to ElementHandle pointing to the frame element.
The method queries page for the selector. If there's no such element within the page, the method will resolve to `null`.
The method queries frame for the selector. If there's no such element within the frame, the method will resolve to `null`.
#### frame.$$(selector)
- `selector` <[string]> Selector to query page for
- returns: <[Promise]<[Array]<[ElementHandle]>>> Promise which resolves to ElementHandles pointing to the frame elements.
The method runs `document.querySelectorAll` within the frame. If no elements match the selector, the return value resolve to `[]`.
#### frame.addScriptTag(url)

@@ -1021,4 +1110,4 @@ - `url` <[string]> Url of a script to be added

- `pageFunction` <[function]|[string]> Function to be evaluated in browser context
- `...args` <...[string]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Object]>> Promise which resolves to function return value
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Serializable]>> Promise which resolves to function return value

@@ -1031,4 +1120,4 @@ If the function, passed to the `page.evaluate`, returns a [Promise], then `page.evaluate` would wait for the promise to resolve and return it's value.

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const result = await page.evaluate(() => {
const page = await browser.newPage();
const result = await page.mainFrame().evaluate(() => {
return Promise.resolve(8 * 7);

@@ -1044,3 +1133,3 @@ });

```js
console.log(await page.evaluate('1 + 2')); // prints "3"
console.log(await page.mainFrame().evaluate('1 + 2')); // prints "3"
```

@@ -1055,3 +1144,3 @@

Returns `true` if the frame has being detached, or `false` otherwise.
Returns `true` if the frame has been detached, or `false` otherwise.

@@ -1073,7 +1162,2 @@ #### frame.name()

#### frame.uploadFile(selector, ...filePaths)
- `selector` <[string]> A [selector] to a file input
- `...filePaths` <[string]> Sets the value of the file input these paths. If some of the `filePaths` are relative paths, then they are resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- returns: <[Promise]> Promise which resolves when the value is set.
#### frame.url()

@@ -1103,4 +1187,4 @@ - returns: <[string]>

- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds).
- `...args` <...[Object]> Arguments to pass to `pageFunction`
- returns: <[Promise]> Promise which resolves when element specified by selector string is added to DOM.
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
- returns: <[Promise]> Promise which resolves when the `pageFunction` returns a truthy value.

@@ -1112,4 +1196,4 @@ The `waitForFunction` could be used to observe viewport size change:

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const watchDog = page.waitForFunction('window.innerWidth < 100');
const page = await browser.newPage();
const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
page.setViewport({width: 50, height: 50});

@@ -1137,5 +1221,7 @@ await watchDog;

puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
let currentURL;
page.waitForSelector('img').then(() => console.log('First URL with image: ' + currentURL));
page.mainFrame()
.waitForSelector('img')
.then(() => console.log('First URL with image: ' + currentURL));
for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])

@@ -1153,8 +1239,9 @@ await page.goto(currentURL);

const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser => {
let page = await browser.newPage();
const page = await browser.newPage();
await page.goto('https://google.com');
let inputElement = await page.$('input[type=submit]');
const inputElement = await page.$('input[type=submit]');
await inputElement.click();
...
// ...
});

@@ -1182,7 +1269,7 @@ ```

- `pageFunction` <[function]> Function to be evaluated in browser context
- `...args` <...[string]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Object]>> Promise which resolves to function return value
- `...args` <...[Serializable]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Serializable]>> Promise which resolves to function return value
If the function, passed to the `elementHandle.evaluate`, returns a [Promise], then `elementHandle.evaluate` would wait for the promise to resolve and return it's value.
The function will be passed in the element ifself as a first argument.
The element will be passed as the first argument to `pageFunction`, followed by any `args`.

@@ -1195,2 +1282,8 @@ #### elementHandle.hover()

#### elementHandle.uploadFile(...filePaths)
- `...filePaths` <...[string]> Sets the value of the file input these paths. If some of the `filePaths` are relative paths, then they are resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd).
- returns: <[Promise]>
This method expects `elementHandle` to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
### class: Request

@@ -1207,4 +1300,2 @@

[Request] class represents requests which are sent by page. [Request] implements [Body] mixin, which in case of HTTP POST requests allows clients to call `request.json()` or `request.text()` to get different representations of request's body.
#### request.abort()

@@ -1248,3 +1339,3 @@

[Response] class represents responses which are received by page. [Response] implements [Body] mixin, which allows clients to call `response.json()` or `response.text()` to get different representations of response body.
[Response] class represents responses which are received by page.

@@ -1276,3 +1367,3 @@ #### response.buffer()

#### response.text()
- returns: <Promise<[text]>> Promise which resolves to a text representation of response body.
- returns: <[Promise]<[string]>> Promise which resolves to a text representation of response body.

@@ -1284,3 +1375,2 @@ #### response.url

[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"

@@ -1310,3 +1400,4 @@ [boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"

[Tracing]: #class-tracing "Tracing"
[ElementHandle]: #class-element "ElementHandle"
[ElementHandle]: #class-elementhandle "ElementHandle"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"

@@ -14,3 +14,4 @@ module.exports = {

"indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2, "outerIIFEBody": 0 }],
"strict": [2, "global"],
}
};

@@ -17,2 +17,4 @@ /**

'use strict';
const puppeteer = require('puppeteer');

@@ -26,3 +28,3 @@

page.on('request', request => {
if (/\.(png|jpg|jpeg$)/.test(request.url))
if (/\.(png|jpg|jpeg|gif|webp)$/i.test(request.url))
request.abort();

@@ -29,0 +31,0 @@ else

@@ -17,7 +17,9 @@ /**

'use strict';
const puppeteer = require('puppeteer');
function sniffDetector() {
let userAgent = window.navigator.userAgent;
let platform = window.navigator.platform;
const userAgent = window.navigator.userAgent;
const platform = window.navigator.platform;

@@ -24,0 +26,0 @@ window.navigator.__defineGetter__('userAgent', function() {

@@ -17,2 +17,4 @@ /**

'use strict';
const puppeteer = require('puppeteer');

@@ -19,0 +21,0 @@

@@ -17,2 +17,4 @@ /**

'use strict';
const puppeteer = require('puppeteer');

@@ -24,3 +26,3 @@ const devices = require('puppeteer/DeviceDescriptors');

const browser = await puppeteer.launch();
let page = await browser.newPage();
const page = await browser.newPage();
await page.emulate(devices['iPhone 6']);

@@ -27,0 +29,0 @@ await page.goto('https://www.nytimes.com/');

@@ -17,2 +17,4 @@ /**

'use strict';
const puppeteer = require('puppeteer');

@@ -19,0 +21,0 @@

@@ -17,2 +17,4 @@ /**

'use strict';
const puppeteer = require('puppeteer');

@@ -19,0 +21,0 @@

@@ -17,2 +17,10 @@ /**

module.exports = require('./lib/Puppeteer');
// If node does not support async await, use the compiled version.
let folder = 'lib';
try {
new Function('async function test(){await 1}');
} catch (error) {
folder = 'node6';
}
module.exports = require(`./${folder}/Puppeteer`);

@@ -18,15 +18,25 @@ /**

const Downloader = require('./utils/ChromiumDownloader');
const platform = Downloader.currentPlatform();
const revision = require('./package').puppeteer.chromium_revision;
const ProgressBar = require('progress');
const revisionInfo = Downloader.revisionInfo(platform, revision);
// Do nothing if the revision is already downloaded.
if (Downloader.revisionInfo(Downloader.currentPlatform(), revision))
if (revisionInfo.downloaded)
return;
let allRevisions = Downloader.downloadedRevisions();
Downloader.downloadRevision(Downloader.currentPlatform(), revision, onProgress)
const allRevisions = Downloader.downloadedRevisions();
Downloader.downloadRevision(platform, revision, onProgress)
// Remove previous chromium revisions.
.then(() => Promise.all(allRevisions.map(({platform, revision}) => Downloader.removeRevision(platform, revision))))
.catch(error => console.error('Download failed: ' + error.message));
.catch(onError);
function onError(error) {
console.error(`ERROR: Failed to download chromium r${revision}!
- Download chromium manually:
${revisionInfo.url}
- Extract chromium into ${revisionInfo.folderPath}
* Chromium executable should be at ${revisionInfo.executablePath}`);
}
let progressBar = null;

@@ -46,5 +56,5 @@ function onProgress(bytesTotal, delta) {

function toMegabytes(bytes) {
let mb = bytes / 1024 / 1024;
return (Math.round(mb * 10) / 10) + ' Mb';
const mb = bytes / 1024 / 1024;
return `${Math.round(mb * 10) / 10} Mb`;
}

@@ -53,3 +53,3 @@ /**

async version() {
let version = await this._connection.send('Browser.getVersion');
const version = await this._connection.send('Browser.getVersion');
return version.product;

@@ -56,0 +56,0 @@ }

@@ -30,3 +30,3 @@ /**

return new Promise((resolve, reject) => {
let ws = new WebSocket(url, { perMessageDeflate: false });
const ws = new WebSocket(url, { perMessageDeflate: false });
ws.on('open', () => resolve(new Connection(url, ws, delay)));

@@ -70,4 +70,4 @@ ws.on('error', reject);

send(method, params = {}) {
let id = ++this._lastId;
let message = JSON.stringify({id, method, params});
const id = ++this._lastId;
const message = JSON.stringify({id, method, params});
debugProtocol('SEND ► ' + message);

@@ -87,5 +87,5 @@ this._ws.send(message);

debugProtocol('◀ RECV ' + message);
let object = JSON.parse(message);
const object = JSON.parse(message);
if (object.id && this._callbacks.has(object.id)) {
let callback = this._callbacks.get(object.id);
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);

@@ -115,6 +115,6 @@ if (object.error)

this._ws.removeAllListeners();
for (let callback of this._callbacks.values())
for (const callback of this._callbacks.values())
callback.reject(new Error(`Protocol error (${callback.method}): Target closed.`));
this._callbacks.clear();
for (let session of this._sessions.values())
for (const session of this._sessions.values())
session._onClosed();

@@ -175,4 +175,4 @@ this._sessions.clear();

return Promise.reject(new Error(`Protocol error (${method}): Session closed. Most likely the page has been closed.`));
let id = ++this._lastId;
let message = JSON.stringify({id, method, params});
const id = ++this._lastId;
const message = JSON.stringify({id, method, params});
debugSession('SEND ► ' + message);

@@ -183,3 +183,3 @@ this._connection.send('Target.sendMessageToTarget', {sessionId: this._sessionId, message}).catch(e => {

return;
let callback = this._callbacks.get(id);
const callback = this._callbacks.get(id);
this._callbacks.delete(object.id);

@@ -198,5 +198,5 @@ callback.reject(e);

debugSession('◀ RECV ' + message);
let object = JSON.parse(message);
const object = JSON.parse(message);
if (object.id && this._callbacks.has(object.id)) {
let callback = this._callbacks.get(object.id);
const callback = this._callbacks.get(object.id);
this._callbacks.delete(object.id);

@@ -221,3 +221,3 @@ if (object.error)

_onClosed() {
for (let callback of this._callbacks.values())
for (const callback of this._callbacks.values())
callback.reject(new Error(`Protocol error (${callback.method}): Target closed.`));

@@ -224,0 +224,0 @@ this._callbacks.clear();

@@ -16,2 +16,3 @@ /**

*/
const path = require('path');
const helper = require('./helper');

@@ -51,7 +52,7 @@

let stringifiedArgs = ['this'];
const stringifiedArgs = ['this'];
stringifiedArgs.push(...args.map(x => JSON.stringify(x)));
let functionDeclaration = `function() { return (${pageFunction})(${stringifiedArgs.join(',')}) }`;
const functionDeclaration = `function() { return (${pageFunction})(${stringifiedArgs.join(',')}) }`;
const objectId = this._remoteObject.objectId;
let { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.callFunctionOn', { objectId, functionDeclaration, returnByValue: false, awaitPromise: true});
const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.callFunctionOn', { objectId, functionDeclaration, returnByValue: false, awaitPromise: true});
if (exceptionDetails)

@@ -66,7 +67,7 @@ throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));

async _visibleCenter() {
let center = await this.evaluate(element => {
const center = await this.evaluate(element => {
if (!element.ownerDocument.contains(element))
return null;
element.scrollIntoViewIfNeeded();
let rect = element.getBoundingClientRect();
const rect = element.getBoundingClientRect();
return {

@@ -86,3 +87,3 @@ x: (Math.max(rect.left, 0) + Math.min(rect.right, window.innerWidth)) / 2,

async hover() {
let {x, y} = await this._visibleCenter();
const {x, y} = await this._visibleCenter();
await this._mouse.move(x, y);

@@ -96,5 +97,15 @@ }

async click(options) {
let {x, y} = await this._visibleCenter();
const {x, y} = await this._visibleCenter();
await this._mouse.click(x, y, options);
}
/**
* @param {!Array<string>} filePaths
* @return {!Promise}
*/
async uploadFile(...filePaths) {
const files = filePaths.map(filePath => path.resolve(filePath));
const objectId = this._remoteObject.objectId;
return this._client.send('DOM.setFileInputFiles', { objectId, files });
}
}

@@ -101,0 +112,0 @@

@@ -18,3 +18,2 @@ /**

const fs = require('fs');
const path = require('path');
const EventEmitter = require('events');

@@ -66,4 +65,4 @@ const helper = require('./helper');

console.assert(parentFrameId);
let parentFrame = this._frames.get(parentFrameId);
let frame = new Frame(this._client, this._mouse, parentFrame, frameId);
const parentFrame = this._frames.get(parentFrameId);
const frame = new Frame(this._client, this._mouse, parentFrame, frameId);
this._frames.set(frame._id, frame);

@@ -83,3 +82,3 @@ this.emit(FrameManager.Events.FrameAttached, frame);

if (frame) {
for (let child of frame.childFrames())
for (const child of frame.childFrames())
this._removeFramesRecursively(child);

@@ -112,3 +111,3 @@ }

_onFrameDetached(frameId) {
let frame = this._frames.get(frameId);
const frame = this._frames.get(frameId);
if (frame)

@@ -124,3 +123,3 @@ this._removeFramesRecursively(frame);

frame._defaultContextId = context.id;
for (let waitTask of frame._waitTasks)
for (const waitTask of frame._waitTasks)
waitTask.rerun();

@@ -133,3 +132,3 @@ }

_removeFramesRecursively(frame) {
for (let child of frame.childFrames())
for (const child of frame.childFrames())
this._removeFramesRecursively(child);

@@ -140,2 +139,10 @@ frame._detach();

}
/**
* @param {!Frame} frame
* @return {boolean}
*/
isMainFrameLoadingFailed() {
return !!this._mainFrame._loadingFailed;
}
}

@@ -182,3 +189,3 @@

async evaluate(pageFunction, ...args) {
let remoteObject = await this._rawEvaluate(pageFunction, ...args);
const remoteObject = await this._rawEvaluate(pageFunction, ...args);
return await helper.serializeRemoteObject(this._client, remoteObject);

@@ -192,6 +199,6 @@ }

async $(selector) {
let remoteObject = await this._rawEvaluate(selector => document.querySelector(selector), selector);
const remoteObject = await this._rawEvaluate(selector => document.querySelector(selector), selector);
if (remoteObject.subtype === 'node')
return new ElementHandle(this._client, remoteObject, this._mouse);
helper.releaseObject(this._client, remoteObject);
await helper.releaseObject(this._client, remoteObject);
return null;

@@ -201,2 +208,25 @@ }

/**
* @param {string} selector
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
const remoteObject = await this._rawEvaluate(selector => Array.from(document.querySelectorAll(selector)), selector);
const response = await this._client.send('Runtime.getProperties', {
objectId: remoteObject.objectId,
ownProperties: true
});
const properties = response.result;
const result = [];
const releasePromises = [helper.releaseObject(this._client, remoteObject)];
for (const property of properties) {
if (property.enumerable && property.value.subtype === 'node')
result.push(new ElementHandle(this._client, property.value, this._mouse));
else
releasePromises.push(helper.releaseObject(this._client, property.value));
}
await Promise.all(releasePromises);
return result;
}
/**
* @param {function()|string} pageFunction

@@ -207,5 +237,5 @@ * @param {!Array<*>} args

async _rawEvaluate(pageFunction, ...args) {
let expression = helper.evaluationString(pageFunction, ...args);
const expression = helper.evaluationString(pageFunction, ...args);
const contextId = this._defaultContextId;
let { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false, awaitPromise: true});
const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false, awaitPromise: true});
if (exceptionDetails)

@@ -277,5 +307,5 @@ throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));

function addScriptTag(url) {
let script = document.createElement('script');
const script = document.createElement('script');
script.src = url;
let promise = new Promise(x => script.onload = x);
const promise = new Promise(x => script.onload = x);
document.head.appendChild(script);

@@ -341,17 +371,2 @@ return promise;

/**
* @param {string} selector
* @param {!Array<string>} filePaths
* @return {!Promise}
*/
async uploadFile(selector, ...filePaths) {
let expression = helper.evaluationString(selector => document.querySelector(selector), selector);
const {result} = await this._client.send('Runtime.evaluate', { expression });
if (!result)
return;
const objectId = result.objectId;
filePaths = filePaths.map(filePath => path.resolve(filePath));
return this._client.send('DOM.setFileInputFiles', { objectId, files: filePaths });
}
/**
* @return {!Promise<string>}

@@ -369,6 +384,7 @@ */

this._url = framePayload.url;
this._loadingFailed = !!framePayload.unreachableUrl;
}
_detach() {
for (let waitTask of this._waitTasks)
for (const waitTask of this._waitTasks)
waitTask.terminate(new Error('waitForSelector failed: frame got detached.'));

@@ -443,2 +459,7 @@ this._detached = true;

// We could have tried to evaluate in a context which was already
// destroyed.
if (error && error.message.includes('Cannot find context with specified id'))
return;
if (error)

@@ -494,3 +515,4 @@ this._reject(error);

childList: true,
subtree: true
subtree: true,
attributes: true
});

@@ -497,0 +519,0 @@ return result;

@@ -42,5 +42,5 @@ /**

if (exceptionDetails.stackTrace) {
for (let callframe of exceptionDetails.stackTrace.callFrames) {
let location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber;
let functionName = callframe.functionName || '<anonymous>';
for (const callframe of exceptionDetails.stackTrace.callFrames) {
const location = callframe.url + ':' + callframe.lineNumber + ':' + callframe.columnNumber;
const functionName = callframe.functionName || '<anonymous>';
message += `\n at ${functionName} (${location})`;

@@ -77,3 +77,3 @@ }

try {
let response = await client.send('Runtime.callFunctionOn', {
const response = await client.send('Runtime.callFunctionOn', {
objectId: remoteObject.objectId,

@@ -117,3 +117,3 @@ functionDeclaration: 'function() { return this; }',

return;
for (let methodName of Reflect.ownKeys(classType.prototype)) {
for (const methodName of Reflect.ownKeys(classType.prototype)) {
const method = Reflect.get(classType.prototype, methodName);

@@ -125,4 +125,4 @@ if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function')

Reflect.set(classType.prototype, methodName, function(...args) {
let argsText = args.map(stringifyArgument).join(', ');
let callsite = `${className}.${methodName}(${argsText})`;
const argsText = args.map(stringifyArgument).join(', ');
const callsite = `${className}.${methodName}(${argsText})`;
if (debug.enabled)

@@ -138,3 +138,3 @@ debug(callsite);

if (apiCoverage) {
for (let event of Object.values(classType.Events))
for (const event of Object.values(classType.Events))
apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, false);

@@ -144,3 +144,3 @@ }

Reflect.set(classType.prototype, 'emit', function(event, ...args) {
let argsText = [JSON.stringify(event)].concat(args.map(stringifyArgument)).join(', ');
const argsText = [JSON.stringify(event)].concat(args.map(stringifyArgument)).join(', ');
if (debug.enabled && this.listenerCount(event))

@@ -169,3 +169,3 @@ debug(`${className}.emit(${argsText})`);

const keys = Object.keys(arg);
for (let key of keys) {
for (const key of keys) {
const value = arg[key];

@@ -195,3 +195,3 @@ if (Helper.isString(value) || Helper.isNumber(value))

static removeEventListeners(listeners) {
for (let listener of listeners)
for (const listener of listeners)
listener.emitter.removeListener(listener.eventName, listener.handler);

@@ -198,0 +198,0 @@ listeners.splice(0, listeners.length);

@@ -34,5 +34,5 @@ /**

*/
async down(key, options) {
let {text} = options || {};
let autoRepeat = this._pressedKeys.has(key);
async down(key, options = {}) {
const text = options.text;
const autoRepeat = this._pressedKeys.has(key);
this._pressedKeys.add(key);

@@ -44,4 +44,4 @@ this._modifiers |= this._modifierBit(key);

windowsVirtualKeyCode: codeForKey(key),
key: key,
text: text,
key,
text,
unmodifiedText: text,

@@ -132,6 +132,6 @@ autoRepeat

*/
async click(x, y, options) {
async click(x, y, options = {}) {
this.move(x, y);
this.down(options);
if (options && options.delay)
if (typeof options.delay === 'number')
await new Promise(f => setTimeout(f, options.delay));

@@ -144,5 +144,3 @@ await this.up(options);

*/
async down(options) {
if (!options)
options = {};
async down(options = {}) {
this._button = (options.button || 'left');

@@ -162,5 +160,3 @@ await this._client.send('Input.dispatchMouseEvent', {

*/
async up(options) {
if (!options)
options = {};
async up(options = {}) {
this._button = 'none';

@@ -178,3 +174,3 @@ await this._client.send('Input.dispatchMouseEvent', {

let keys = {
const keys = {
'Cancel': 3,

@@ -181,0 +177,0 @@ 'Help': 6,

@@ -16,4 +16,5 @@ /**

*/
const os = require('os');
const path = require('path');
const removeRecursive = require('rimraf').sync;
const removeSync = require('rimraf').sync;
const childProcess = require('child_process');

@@ -24,5 +25,7 @@ const Downloader = require('../utils/ChromiumDownloader');

const readline = require('readline');
const fs = require('fs');
const helper = require('./helper');
const ChromiumRevision = require('../package.json').puppeteer.chromium_revision;
const CHROME_PROFILE_PATH = path.resolve(__dirname, '..', '.dev_profile');
let browserId = 0;
const CHROME_PROFILE_PATH = path.join(os.tmpdir(), 'puppeteer_dev_profile-');

@@ -39,2 +42,3 @@ const DEFAULT_ARGS = [

'--enable-automation',
'--enable-devtools-experiments',
'--metrics-recording-only',

@@ -50,3 +54,3 @@ '--no-first-run',

/**
* @param {!Object} options
* @param {!Object=} options
* @return {!Promise<!Browser>}

@@ -56,5 +60,5 @@ */

options = options || {};
++browserId;
let userDataDir = CHROME_PROFILE_PATH + browserId;
let chromeArguments = DEFAULT_ARGS.concat([
const userDataDir = fs.mkdtempSync(CHROME_PROFILE_PATH);
const chromeArguments = DEFAULT_ARGS.concat([
`--user-data-dir=${userDataDir}`,

@@ -64,5 +68,6 @@ ]);

chromeArguments.push(
`--headless`,
`--disable-gpu`,
`--hide-scrollbars`
'--headless',
'--disable-gpu',
'--hide-scrollbars',
'--mute-audio'
);

@@ -72,5 +77,4 @@ }

if (typeof chromeExecutable !== 'string') {
let chromiumRevision = require('../package.json').puppeteer.chromium_revision;
let revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), chromiumRevision);
console.assert(revisionInfo, 'Chromium revision is not downloaded. Run npm install');
const revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), ChromiumRevision);
console.assert(revisionInfo.downloaded, `Chromium revision is not downloaded. Run "npm install"`);
chromeExecutable = revisionInfo.executablePath;

@@ -80,3 +84,4 @@ }

chromeArguments.push(...options.args);
let chromeProcess = childProcess.spawn(chromeExecutable, chromeArguments, {});
const chromeProcess = childProcess.spawn(chromeExecutable, chromeArguments, {});
if (options.dumpio) {

@@ -86,26 +91,30 @@ chromeProcess.stdout.pipe(process.stdout);

}
let stderr = '';
chromeProcess.stderr.on('data', data => stderr += data.toString('utf8'));
// Cleanup as processes exit.
const onProcessExit = () => chromeProcess.kill();
process.on('exit', onProcessExit);
let terminated = false;
chromeProcess.on('exit', () => {
terminated = true;
process.removeListener('exit', onProcessExit);
removeRecursive(userDataDir);
});
let killed = false;
process.once('exit', killChrome);
chromeProcess.once('close', () => removeSync(userDataDir));
let browserWSEndpoint = await waitForWSEndpoint(chromeProcess);
if (terminated)
throw new Error('Failed to launch chrome! ' + stderr);
// Failed to connect to browser.
if (!browserWSEndpoint) {
chromeProcess.kill();
throw new Error('Failed to connect to chrome!');
if (options.handleSIGINT !== false)
process.once('SIGINT', killChrome);
try {
const connectionDelay = options.slowMo || 0;
const browserWSEndpoint = await waitForWSEndpoint(chromeProcess, options.timeout || 30 * 1000);
const connection = await Connection.create(browserWSEndpoint, connectionDelay);
return new Browser(connection, !!options.ignoreHTTPSErrors, killChrome);
} catch (e) {
killChrome();
throw e;
}
let connectionDelay = options.slowMo || 0;
let connection = await Connection.create(browserWSEndpoint, connectionDelay);
return new Browser(connection, !!options.ignoreHTTPSErrors, () => chromeProcess.kill());
function killChrome() {
if (killed)
return;
killed = true;
if (process.platform === 'win32')
childProcess.execSync(`taskkill /pid ${chromeProcess.pid} /T /F`);
else
chromeProcess.kill('SIGKILL');
}
}

@@ -118,3 +127,3 @@

static async connect({browserWSEndpoint, ignoreHTTPSErrors = false}) {
let connection = await Connection.create(browserWSEndpoint);
const connection = await Connection.create(browserWSEndpoint);
return new Browser(connection, !!ignoreHTTPSErrors);

@@ -126,10 +135,32 @@ }

* @param {!ChildProcess} chromeProcess
* @param {number} timeout
* @return {!Promise<string>}
*/
function waitForWSEndpoint(chromeProcess) {
return new Promise(fulfill => {
function waitForWSEndpoint(chromeProcess, timeout) {
return new Promise((resolve, reject) => {
const rl = readline.createInterface({ input: chromeProcess.stderr });
rl.on('line', onLine);
rl.once('close', () => fulfill(''));
let stderr = '';
const listeners = [
helper.addEventListener(rl, 'line', onLine),
helper.addEventListener(rl, 'close', onClose),
helper.addEventListener(chromeProcess, 'exit', onClose)
];
const timeoutId = timeout ? setTimeout(onTimeout, timeout) : 0;
function onClose() {
cleanup();
reject(new Error([
'Failed to launch chrome!',
stderr,
'',
'TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md',
'',
].join('\n')));
}
function onTimeout() {
cleanup();
reject(new Error(`Timed out after ${timeout} ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r${ChromiumRevision}`));
}
/**

@@ -139,8 +170,15 @@ * @param {string} line

function onLine(line) {
stderr += line + '\n';
const match = line.match(/^DevTools listening on (ws:\/\/.*)$/);
if (!match)
return;
rl.removeListener('line', onLine);
fulfill(match[1]);
cleanup();
resolve(match[1]);
}
function cleanup() {
if (timeoutId)
clearTimeout(timeoutId);
helper.removeEventListeners(listeners);
}
});

@@ -147,0 +185,0 @@ }

@@ -64,3 +64,3 @@ /**

hasValue(key, value) {
let set = this._map.get(key);
const set = this._map.get(key);
if (!set)

@@ -84,4 +84,4 @@ return false;

delete(key, value) {
let values = this.get(key);
let result = values.delete(value);
const values = this.get(key);
const result = values.delete(value);
if (!values.size)

@@ -104,3 +104,3 @@ this._map.delete(key);

firstValue(key) {
let set = this._map.get(key);
const set = this._map.get(key);
if (!set)

@@ -122,4 +122,4 @@ return null;

valuesArray() {
let result = [];
for (let key of this._map.keys())
const result = [];
for (const key of this._map.keys())
result.push(...Array.from(this._map.get(key).values()));

@@ -126,0 +126,0 @@ return result;

@@ -43,8 +43,8 @@ /**

let watchdog = new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._timeout))
const watchdog = new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._timeout))
.then(() => 'Navigation Timeout Exceeded: ' + this._timeout + 'ms exceeded');
let navigationPromises = [watchdog];
const navigationPromises = [watchdog];
if (!this._ignoreHTTPSErrors) {
let certificateError = new Promise(fulfill => {
const certificateError = new Promise(fulfill => {
this._eventListeners.push(helper.addEventListener(this._client, 'Security.certificateError', fulfill));

@@ -56,3 +56,3 @@ }).then(error => 'SSL Certificate error: ' + error.errorType);

if (this._waitUntil === 'load') {
let loadEventFired = new Promise(fulfill => {
const loadEventFired = new Promise(fulfill => {
this._eventListeners.push(helper.addEventListener(this._client, 'Page.loadEventFired', fulfill));

@@ -69,3 +69,3 @@ }).then(() => null);

]);
let networkIdle = new Promise(fulfill => this._networkIdleCallback = fulfill).then(() => null);
const networkIdle = new Promise(fulfill => this._networkIdleCallback = fulfill).then(() => null);
navigationPromises.push(networkIdle);

@@ -72,0 +72,0 @@ }

@@ -54,4 +54,4 @@ /**

this._extraHTTPHeaders = new Map(extraHTTPHeaders);
let headers = {};
for (let entry of extraHTTPHeaders.entries())
const headers = {};
for (const entry of extraHTTPHeaders.entries())
headers[entry[0]] = entry[1];

@@ -93,3 +93,3 @@ await this._client.send('Network.setExtraHTTPHeaders', { headers });

if (event.redirectStatusCode) {
let request = this._interceptionIdToRequest.get(event.interceptionId);
const request = this._interceptionIdToRequest.get(event.interceptionId);
console.assert(request, 'INTERNAL ERROR: failed to find request for interception redirect.');

@@ -100,3 +100,3 @@ this._handleRequestRedirect(request, event.redirectStatusCode, event.redirectHeaders);

}
let requestHash = generateRequestHash(event.request);
const requestHash = generateRequestHash(event.request);
this._requestHashToInterceptions.set(requestHash, event);

@@ -112,3 +112,3 @@ this._maybeResolveInterception(requestHash);

_handleRequestRedirect(request, redirectStatus, redirectHeaders) {
let response = new Response(this._client, request, redirectStatus, redirectHeaders);
const response = new Response(this._client, request, redirectStatus, redirectHeaders);
request._response = response;

@@ -128,3 +128,3 @@ this._requestIdToRequest.delete(request._requestId);

_handleRequestStart(requestId, interceptionId, url, requestPayload) {
let request = new Request(this._client, requestId, interceptionId, url, requestPayload);
const request = new Request(this._client, requestId, interceptionId, url, requestPayload);
this._requestIdToRequest.set(requestId, request);

@@ -143,3 +143,3 @@ this._interceptionIdToRequest.set(interceptionId, request);

return;
let requestHash = generateRequestHash(event.request);
const requestHash = generateRequestHash(event.request);
this._requestHashToRequestIds.set(requestHash, event.requestId);

@@ -150,3 +150,3 @@ this._maybeResolveInterception(requestHash);

if (event.redirectResponse) {
let request = this._requestIdToRequest.get(event.requestId);
const request = this._requestIdToRequest.get(event.requestId);
this._handleRequestRedirect(request, event.redirectResponse.status, event.redirectResponse.headers);

@@ -175,7 +175,7 @@ }

_onResponseReceived(event) {
let request = this._requestIdToRequest.get(event.requestId);
const request = this._requestIdToRequest.get(event.requestId);
// FileUpload sends a response without a matching request.
if (!request)
return;
let response = new Response(this._client, request, event.response.status, event.response.headers);
const response = new Response(this._client, request, event.response.status, event.response.headers);
request._response = response;

@@ -189,3 +189,3 @@ this.emit(NetworkManager.Events.Response, response);

_onLoadingFinished(event) {
let request = this._requestIdToRequest.get(event.requestId);
const request = this._requestIdToRequest.get(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.

@@ -205,3 +205,3 @@ // @see https://crbug.com/750469

_onLoadingFailed(event) {
let request = this._requestIdToRequest.get(event.requestId);
const request = this._requestIdToRequest.get(event.requestId);
// For certain requestIds we never receive requestWillBeSent event.

@@ -262,3 +262,3 @@ // @see https://crbug.com/750469

headers = {};
for (let entry of overrides.headers)
for (const entry of overrides.headers)
headers[entry[0]] = entry[1];

@@ -314,3 +314,3 @@ }

this._contentPromise = this._request._completePromise.then(async() => {
let response = await this._client.send('Network.getResponseBody', {
const response = await this._client.send('Network.getResponseBody', {
requestId: this._request._requestId

@@ -328,3 +328,3 @@ });

async text() {
let content = await this.buffer();
const content = await this.buffer();
return content.toString('utf8');

@@ -337,3 +337,3 @@ }

async json() {
let content = await this.text();
const content = await this.text();
return JSON.parse(content);

@@ -356,3 +356,3 @@ }

function generateRequestHash(request) {
let hash = {
const hash = {
url: request.url,

@@ -363,5 +363,5 @@ method: request.method,

};
let headers = Object.keys(request.headers);
const headers = Object.keys(request.headers);
headers.sort();
for (let header of headers) {
for (const header of headers) {
if (header === 'Accept' || header === 'Referer' || header === 'X-DevTools-Emulate-Network-Conditions-Client-Id')

@@ -379,3 +379,3 @@ continue;

function removeURLHash(url) {
let urlObject = new URL(url);
const urlObject = new URL(url);
urlObject.hash = '';

@@ -382,0 +382,0 @@ return urlObject.toString();

@@ -150,2 +150,49 @@ /**

/**
* @param {string} selector
* @return {!Promise<!Array<!ElementHandle>>}
*/
async $$(selector) {
return this.mainFrame().$$(selector);
}
/**
* @param {!Array<string>} urls
* @return {!Promise<!Array<Network.Cookie>>}
*/
async cookies(...urls) {
return (await this._client.send('Network.getCookies', {
urls: urls.length ? urls : [this.url()]
})).cookies;
}
/**
* @param {Array<Network.CookieParam>} cookies
*/
async deleteCookie(...cookies) {
const pageURL = this.url();
for (const cookie of cookies) {
const item = Object.assign({}, cookie);
if (!cookie.url && pageURL.startsWith('http'))
item.url = pageURL;
await this._client.send('Network.deleteCookies', item);
}
}
/**
* @param {Array<Network.CookieParam>} cookies
*/
async setCookie(...cookies) {
const items = cookies.map(cookie => {
const item = Object.assign({}, cookie);
const pageURL = this.url();
if (!item.url && pageURL.startsWith('http'))
item.url = this.url();
return item;
});
await this.deleteCookie(...items);
if (items.length)
await this._client.send('Network.setCookies', { cookies: items });
}
/**
* @param {string} url

@@ -175,3 +222,3 @@ * @return {!Promise}

let expression = helper.evaluationString(addPageBinding, name);
const expression = helper.evaluationString(addPageBinding, name);
await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source: expression });

@@ -218,3 +265,3 @@ await this._client.send('Runtime.evaluate', { expression, returnByValue: true });

_handleException(exceptionDetails) {
let message = helper.getExceptionMessage(exceptionDetails);
const message = helper.getExceptionMessage(exceptionDetails);
this.emit(Page.Events.PageError, new Error(message));

@@ -225,5 +272,5 @@ }

if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:page-binding') {
let {name, seq, args} = JSON.parse(event.args[1].value);
let result = await this._pageBindings[name](...args);
let expression = helper.evaluationString(deliverResult, name, seq, result);
const {name, seq, args} = JSON.parse(event.args[1].value);
const result = await this._pageBindings[name](...args);
const expression = helper.evaluationString(deliverResult, name, seq, result);
this._client.send('Runtime.evaluate', { expression });

@@ -241,3 +288,3 @@

}
let values = await Promise.all(event.args.map(arg => helper.serializeRemoteObject(this._client, arg)));
const values = await Promise.all(event.args.map(arg => helper.serializeRemoteObject(this._client, arg)));
this.emit(Page.Events.Console, ...values);

@@ -257,3 +304,3 @@ }

console.assert(dialogType, 'Unknown javascript dialog type: ' + event.type);
let dialog = new Dialog(this._client, dialogType, event.message, event.defaultPrompt);
const dialog = new Dialog(this._client, dialogType, event.message, event.defaultPrompt);
this.emit(Page.Events.Dialog, dialog);

@@ -270,2 +317,16 @@ }

/**
* @return {!Promise<String>}
*/
async content() {
return await this.evaluate(() => {
let retVal = '';
if (document.doctype)
retVal = new XMLSerializer().serializeToString(document.doctype);
if (document.documentElement)
retVal += document.documentElement.outerHTML;
return retVal;
});
}
/**
* @param {string} html

@@ -303,8 +364,5 @@ * @return {!Promise}

helper.removeEventListeners([listener]);
if (url === 'about:blank')
return null;
let response = responses.get(this.mainFrame().url());
if (!response)
if (this._frameManager.isMainFrameLoadingFailed())
throw new Error('Failed to navigate: ' + url);
return response;
return responses.get(this.mainFrame().url()) || null;
}

@@ -378,2 +436,18 @@

/**
* @param {boolean} enabled
*/
async setJavaScriptEnabled(enabled) {
await this._client.send('Emulation.setScriptExecutionDisabled', { value: !enabled });
}
/**
* @param {?string} mediaType
* @return {!Promise}
*/
async emulateMedia(mediaType) {
console.assert(mediaType === 'screen' || mediaType === 'print' || mediaType === null, 'Unsupported media type: ' + mediaType);
await this._client.send('Emulation.setEmulatedMedia', {media: mediaType || ''});
}
/**
* @param {!Page.Viewport} viewport

@@ -411,3 +485,3 @@ * @return {!Promise}

async evaluateOnNewDocument(pageFunction, ...args) {
let source = helper.evaluationString(pageFunction, ...args);
const source = helper.evaluationString(pageFunction, ...args);
await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source });

@@ -420,7 +494,6 @@ }

*/
async screenshot(options) {
options = options || {};
async screenshot(options = {}) {
let screenshotType = null;
if (options.path) {
let mimeType = mime.lookup(options.path);
const mimeType = mime.lookup(options.path);
if (mimeType === 'image/png')

@@ -481,3 +554,7 @@ screenshotType = 'png';

let result = await this._client.send('Page.captureScreenshot', { format, quality: options.quality, clip });
if (options.omitBackground)
await this._client.send('Emulation.setDefaultBackgroundColorOverride', { color: { r: 0, g: 0, b: 0, a: 0 } });
const result = await this._client.send('Page.captureScreenshot', { format, quality: options.quality, clip });
if (options.omitBackground)
await this._client.send('Emulation.setDefaultBackgroundColorOverride');

@@ -487,3 +564,3 @@ if (options.fullPage)

let buffer = new Buffer(result.data, 'base64');
const buffer = new Buffer(result.data, 'base64');
if (options.path)

@@ -495,19 +572,16 @@ fs.writeFileSync(options.path, buffer);

/**
* @param {string} filePath
* @param {!Object=} options
* @return {!Promise<!Buffer>}
*/
async pdf(options) {
options = options || {};
async pdf(options = {}) {
const scale = options.scale || 1;
const displayHeaderFooter = !!options.displayHeaderFooter;
const printBackground = !!options.printBackground;
const landscape = !!options.landscape;
const pageRanges = options.pageRanges || '';
let scale = options.scale || 1;
let displayHeaderFooter = options.displayHeaderFooter || false;
let printBackground = options.printBackground || true;
let landscape = options.landscape || false;
let pageRanges = options.pageRanges || '';
let paperWidth = 8.5;
let paperHeight = 11;
if (options.format) {
let format = Page.PaperFormats[options.format.toLowerCase()];
const format = Page.PaperFormats[options.format.toLowerCase()];
console.assert(format, 'Unknown paper format: ' + options.format);

@@ -521,9 +595,9 @@ paperWidth = format.width;

let marginOptions = options.margin || {};
let marginTop = convertPrintParameterToInches(marginOptions.top) || 0;
let marginLeft = convertPrintParameterToInches(marginOptions.left) || 0;
let marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0;
let marginRight = convertPrintParameterToInches(marginOptions.right) || 0;
const marginOptions = options.margin || {};
const marginTop = convertPrintParameterToInches(marginOptions.top) || 0;
const marginLeft = convertPrintParameterToInches(marginOptions.left) || 0;
const marginBottom = convertPrintParameterToInches(marginOptions.bottom) || 0;
const marginRight = convertPrintParameterToInches(marginOptions.right) || 0;
let result = await this._client.send('Page.printToPDF', {
const result = await this._client.send('Page.printToPDF', {
landscape: landscape,

@@ -541,3 +615,3 @@ displayHeaderFooter: displayHeaderFooter,

});
let buffer = new Buffer(result.data, 'base64');
const buffer = new Buffer(result.data, 'base64');
if (options.path)

@@ -582,3 +656,3 @@ fs.writeFileSync(options.path, buffer);

async click(selector, options) {
let handle = await this.$(selector);
const handle = await this.$(selector);
console.assert(handle, 'No node found for selector: ' + selector);

@@ -594,3 +668,3 @@ await handle.click(options);

async hover(selector) {
let handle = await this.$(selector);
const handle = await this.$(selector);
console.assert(handle, 'No node found for selector: ' + selector);

@@ -606,3 +680,3 @@ await handle.hover();

async focus(selector) {
let handle = await this.$(selector);
const handle = await this.$(selector);
console.assert(handle, 'No node found for selector: ' + selector);

@@ -623,3 +697,3 @@ await handle.evaluate(element => element.focus());

let last;
for (let char of text) {
for (const char of text) {
last = this.press(char, {text: char, delay});

@@ -671,11 +745,2 @@ if (delay)

}
/**
* @param {string} selector
* @param {!Array<string>} filePaths
* @return {!Promise}
*/
async uploadFile(selector, ...filePaths) {
return this.mainFrame().uploadFile(selector, ...filePaths);
}
}

@@ -697,3 +762,3 @@

let unitToPixels = {
const unitToPixels = {
'px': 1,

@@ -717,3 +782,3 @@ 'in': 96,

} else if (helper.isString(parameter)) {
let text = parameter;
const text = parameter;
let unit = text.substring(text.length - 2).toLowerCase();

@@ -729,3 +794,3 @@ let valueText = '';

}
let value = Number(valueText);
const value = Number(valueText);
console.assert(!isNaN(value), 'Failed to parse parameter value: ' + text);

@@ -759,3 +824,32 @@ pixels = value * unitToPixels[unit];

/**
* @typedef {Object} Network.Cookie
* @property {string} name
* @property {string} value
* @property {string} domain
* @property {string} path
* @property {number} expires
* @property {number} size
* @property {boolean} httpOnly
* @property {boolean} secure
* @property {boolean} session
* @property {("Strict"|"Lax")=} sameSite
*/
/**
* @typedef {Object} Network.CookieParam
* @property {string} name
* @property {string=} value
* @property {string=} url
* @property {string=} domain
* @property {string=} path
* @property {number=} expires
* @property {boolean=} httpOnly
* @property {boolean=} secure
* @property {("Strict"|"Lax")=} sameSite
*/
module.exports = Page;
helper.tracePublicAPI(Page);

@@ -60,3 +60,3 @@ /**

let fulfill;
let contentPromise = new Promise(x => fulfill = x);
const contentPromise = new Promise(x => fulfill = x);
this._client.once('Tracing.tracingComplete', event => {

@@ -77,5 +77,5 @@ this._readStream(event.stream, this._path).then(fulfill);

let eof = false;
let file = fs.openSync(path, 'w');
const file = fs.openSync(path, 'w');
while (!eof) {
let response = await this._client.send('IO.read', {handle});
const response = await this._client.send('IO.read', {handle});
eof = response.eof;

@@ -82,0 +82,0 @@ if (path)

{
"name": "puppeteer",
"version": "0.9.0",
"version": "0.10.0",
"description": "A high-level API to control headless Chrome over the DevTools Protocol",

@@ -8,3 +8,3 @@ "main": "index.js",

"engines": {
"node": ">=7.10.0"
"node": ">=6.4.0"
},

@@ -16,7 +16,11 @@ "scripts": {

"test-doclint": "jasmine utils/doclint/check_public_api/test/test.js && jasmine utils/doclint/preprocessor/test.js",
"test": "npm run lint --silent && npm run coverage && npm run test-phantom && npm run test-doclint",
"test": "npm run lint --silent && npm run coverage && npm run test-phantom && npm run test-doclint && npm run test-node6",
"install": "node install.js",
"lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run doc",
"doc": "node utils/doclint/cli.js",
"coverage": "COVERAGE=true npm run unit"
"coverage": "COVERAGE=true npm run unit",
"node6": "node utils/node6-transform/index.js",
"test-node6": "jasmine utils/node6-transform/test/test.js",
"build": "npm run node6",
"node6-sanity": "jasmine test/sanity.js"
},

@@ -28,4 +32,6 @@ "author": "The Chromium Authors",

"extract-zip": "^1.6.5",
"https-proxy-agent": "^2.1.0",
"mime": "^1.3.4",
"progress": "^2.0.0",
"proxy-from-env": "^1.0.0",
"rimraf": "^2.6.1",

@@ -35,3 +41,3 @@ "ws": "^3.0.0"

"puppeteer": {
"chromium_revision": "494755"
"chromium_revision": "496140"
},

@@ -38,0 +44,0 @@ "devDependencies": {

@@ -1,4 +0,4 @@

# Puppeteer [![Build Status](https://travis-ci.com/GoogleChrome/puppeteer.svg?token=8jabovWqb8afz5RDcYqx&branch=master)](https://travis-ci.com/GoogleChrome/puppeteer) [![NPM puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer)
# Puppeteer [![Build Status](https://travis-ci.org/GoogleChrome/puppeteer.svg?branch=master)](https://travis-ci.org/GoogleChrome/puppeteer) [![NPM puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer)
<img src="https://user-images.githubusercontent.com/39191/29330067-dfc2be5a-81ac-11e7-9cc2-c569dd5f078c.png" height="200" align="right">
<img src="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200" align="right">

@@ -32,7 +32,7 @@ ###### [API](docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/GoogleChrome/puppeteer/blob/master/CONTRIBUTING.md)

> **Note**: When you install Puppeteer, it downloads a recent version of Chromium (~71Mb Mac, ~90Mb Linux, ~110Mb Win) that is guaranteed to work with the API.
> **Note**: When you install Puppeteer, it downloads a recent version of Chromium (~71Mb Mac, ~90Mb Linux, ~110Mb Win) that is guaranteed to work with the API.
### Usage
Puppeteer will be familiar to using other browser testing frameworks. You create an instance
Puppeteer will be familiar to people using other browser testing frameworks. You create an instance
of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#).

@@ -45,10 +45,9 @@

(async() => {
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
browser.close();
browser.close();
})();

@@ -64,10 +63,9 @@ ```

(async() => {
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
browser.close();
browser.close();
})();

@@ -78,2 +76,29 @@ ```

**Example** - evaluate script in the context of the page
```js
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
console.log('Dimensions:', dimensions);
browser.close();
})();
```
See [`Page.evaluate()`](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`.
## Default runtime settings

@@ -83,6 +108,6 @@

Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the ['headless' option](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions) when creating a browser:
Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the ['headless' option](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions) when launching a browser:
```js
const browser = await puppeteer.launch({headless: false});
const browser = await puppeteer.launch({headless: false}); // default is true
```

@@ -110,2 +135,35 @@

## Debugging tips
1. Turn off headless mode - sometimes it's useful to see what the browser is
displaying. Instead of launching in headless mode, launch a full version of
Chrome using `headless: false`:
```js
const browser = await puppeteer.launch({headless: false});
```
1. Slow it down - the `slowMo` option slows down Puppeteer operations by the
specified amount of milliseconds. It's another way to help see what's going on.
```js
const browser = await puppeteer.launch({
headless: false,
slowMo: 250 // slow down by 250ms
});
```
2. Capture console output from the page by listening for the `console` event.
This is also handy when debugging code in `page.evaluate()`:
```js
page.on('console', (...args) => {
console.log('PAGE LOG:', ...args);
});
await page.evaluate(() => {
console.log(`url is ${location.href}`);
});
```
## Contributing to Puppeteer

@@ -123,3 +181,2 @@

#### Q: What is the difference between Puppeteer, Selenium / WebDriver, and PhantomJS?

@@ -133,3 +190,2 @@

#### Q: Who maintains Puppeteer?

@@ -152,5 +208,5 @@

#### Q: How does Puppeteer compare with other headless Chrome projects?
The past few months have brought [several new libraries for automating headless Chrome](https://medium.com/@kensoh/chromeless-chrominator-chromy-navalia-lambdium-ghostjs-autogcd-ef34bcd26907). As the team authoring the underlying DevTools Protocol, we're excited to witness and support this flourishing ecosystem.
We've reached out to a number of these projects to see if there are opportunities for collaboration, and we're happy to do what we can to help.

@@ -62,4 +62,4 @@ #!/usr/bin/env node

let fromRevision = parseInt(process.argv[2], 10);
let toRevision = parseInt(process.argv[3], 10);
const fromRevision = parseInt(process.argv[2], 10);
const toRevision = parseInt(process.argv[3], 10);
checkRangeAvailability(fromRevision, toRevision);

@@ -72,3 +72,3 @@

console.log('Fetching revisions from ' + OMAHA_PROXY);
let platforms = await loadJSON(OMAHA_PROXY);
const platforms = await loadJSON(OMAHA_PROXY);
if (!platforms) {

@@ -78,14 +78,14 @@ console.error('ERROR: failed to fetch chromium revisions from omahaproxy.');

}
let table = new Table([27, 7, 7, 7, 7]);
const table = new Table([27, 7, 7, 7, 7]);
table.drawRow([''].concat(Downloader.supportedPlatforms()));
for (let platform of platforms) {
for (const platform of platforms) {
// Trust only to the main platforms.
if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux')
continue;
let osName = platform.os === 'win' ? 'win32' : platform.os;
for (let version of platform.versions) {
const osName = platform.os === 'win' ? 'win32' : platform.os;
for (const version of platform.versions) {
if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable')
continue;
let revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15);
let revision = parseInt(version.branch_base_position, 10);
const revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15);
const revision = parseInt(version.branch_base_position, 10);
await checkAndDrawRevisionAvailability(table, revisionName, revision);

@@ -102,5 +102,5 @@ }

async function checkRangeAvailability(fromRevision, toRevision) {
let table = new Table([10, 7, 7, 7, 7]);
const table = new Table([10, 7, 7, 7, 7]);
table.drawRow([''].concat(Downloader.supportedPlatforms()));
let inc = fromRevision < toRevision ? 1 : -1;
const inc = fromRevision < toRevision ? 1 : -1;
for (let revision = fromRevision; revision !== toRevision; revision += inc)

@@ -117,11 +117,11 @@ await checkAndDrawRevisionAvailability(table, '', revision);

async function checkAndDrawRevisionAvailability(table, name, revision) {
let promises = [];
for (let platform of Downloader.supportedPlatforms())
const promises = [];
for (const platform of Downloader.supportedPlatforms())
promises.push(Downloader.canDownloadRevision(platform, revision));
let availability = await Promise.all(promises);
let allAvailable = availability.every(e => !!e);
let values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)];
const availability = await Promise.all(promises);
const allAvailable = availability.every(e => !!e);
const values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)];
for (let i = 0; i < availability.length; ++i) {
let decoration = availability[i] ? '+' : '-';
let color = availability[i] ? colors.green : colors.red;
const decoration = availability[i] ? '+' : '-';
const color = availability[i] ? colors.green : colors.red;
values.push(color + decoration + colors.reset);

@@ -138,3 +138,3 @@ }

let resolve;
let promise = new Promise(x => resolve = x);
const promise = new Promise(x => resolve = x);
https.get(url, response => {

@@ -150,3 +150,3 @@ if (response.statusCode !== 200) {

response.on('end', function(){
let json = JSON.parse(body);
const json = JSON.parse(body);
resolve(json);

@@ -174,4 +174,4 @@ });

function filterOutColors(text) {
for (let colorName in colors) {
let color = colors[colorName];
for (const colorName in colors) {
const color = colors[colorName];
text = text.replace(color, '');

@@ -188,3 +188,3 @@ }

function padLeft(text, length) {
let printableCharacters = filterOutColors(text);
const printableCharacters = filterOutColors(text);
return printableCharacters.length >= length ? text : spaceString(length - text.length) + text;

@@ -199,8 +199,8 @@ }

function padCenter(text, length) {
let printableCharacters = filterOutColors(text);
const printableCharacters = filterOutColors(text);
if (printableCharacters.length >= length)
return text;
let left = Math.floor((length - printableCharacters.length) / 2);
let right = Math.ceil((length - printableCharacters.length) / 2);
const left = Math.floor((length - printableCharacters.length) / 2);
const right = Math.ceil((length - printableCharacters.length) / 2);
return spaceString(left) + text + spaceString(right);
}

@@ -25,2 +25,4 @@ /**

const removeRecursive = require('rimraf');
const ProxyAgent = require('https-proxy-agent');
const getProxyForUrl = require('proxy-from-env').getProxyForUrl;

@@ -48,3 +50,3 @@ const DOWNLOADS_FOLDER = path.join(__dirname, '..', '.local-chromium');

currentPlatform: function() {
let platform = os.platform();
const platform = os.platform();
if (platform === 'darwin')

@@ -66,11 +68,8 @@ return 'mac';

console.assert(downloadURLs[platform], 'Unknown platform: ' + platform);
let url = URL.parse(util.format(downloadURLs[platform], revision));
let options = {
method: 'HEAD',
host: url.host,
path: url.pathname,
};
const options = requestOptions(util.format(downloadURLs[platform], revision), 'HEAD');
let resolve;
let promise = new Promise(x => resolve = x);
let request = https.request(options, response => {
const promise = new Promise(x => resolve = x);
const request = https.request(options, response => {
resolve(response.statusCode === 200);

@@ -92,19 +91,19 @@ });

*/
downloadRevision: async function(platform, revision, progressCallback) {
downloadRevision: function(platform, revision, progressCallback) {
let url = downloadURLs[platform];
console.assert(url, `Unsupported platform: ${platform}`);
url = util.format(url, revision);
let zipPath = path.join(DOWNLOADS_FOLDER, `download-${platform}-${revision}.zip`);
let folderPath = getFolderPath(platform, revision);
const zipPath = path.join(DOWNLOADS_FOLDER, `download-${platform}-${revision}.zip`);
const folderPath = getFolderPath(platform, revision);
if (fs.existsSync(folderPath))
return;
try {
if (!fs.existsSync(DOWNLOADS_FOLDER))
fs.mkdirSync(DOWNLOADS_FOLDER);
await downloadFile(url, zipPath, progressCallback);
await extractZip(zipPath, folderPath);
} finally {
if (fs.existsSync(zipPath))
fs.unlinkSync(zipPath);
}
if (!fs.existsSync(DOWNLOADS_FOLDER))
fs.mkdirSync(DOWNLOADS_FOLDER);
return downloadFile(url, zipPath, progressCallback)
.then(() => extractZip(zipPath, folderPath))
.catch(err => err)
.then(() => {
if (fs.existsSync(zipPath))
fs.unlinkSync(zipPath);
});
},

@@ -118,3 +117,3 @@

return [];
let fileNames = fs.readdirSync(DOWNLOADS_FOLDER);
const fileNames = fs.readdirSync(DOWNLOADS_FOLDER);
return fileNames.map(fileName => parseFolderPath(fileName)).filter(revision => !!revision);

@@ -126,8 +125,9 @@ },

* @param {string} revision
* @return {!Promise}
*/
removeRevision: async function(platform, revision) {
removeRevision: function(platform, revision) {
console.assert(downloadURLs[platform], `Unsupported platform: ${platform}`);
const folderPath = getFolderPath(platform, revision);
console.assert(fs.existsSync(folderPath));
await new Promise(fulfill => removeRecursive(folderPath, fulfill));
return new Promise(fulfill => removeRecursive(folderPath, fulfill));
},

@@ -138,9 +138,7 @@

* @param {string} revision
* @return {?{executablePath: string}}
* @return {!{folderPath: string, executablePath: string, downloaded: boolean, url: string}}
*/
revisionInfo: function(platform, revision) {
console.assert(downloadURLs[platform], `Unsupported platform: ${platform}`);
let folderPath = getFolderPath(platform, revision);
if (!fs.existsSync(folderPath))
return null;
const folderPath = getFolderPath(platform, revision);
let executablePath = '';

@@ -156,3 +154,6 @@ if (platform === 'mac')

return {
executablePath: executablePath
executablePath,
folderPath,
downloaded: fs.existsSync(folderPath),
url: util.format(downloadURLs[platform], revision)
};

@@ -176,7 +177,7 @@ },

function parseFolderPath(folderPath) {
let name = path.basename(folderPath);
let splits = name.split('-');
const name = path.basename(folderPath);
const splits = name.split('-');
if (splits.length !== 2)
return null;
let [platform, revision] = splits;
const [platform, revision] = splits;
if (!downloadURLs[platform])

@@ -195,6 +196,9 @@ return null;

let fulfill, reject;
let promise = new Promise((x, y) => { fulfill = x; reject = y; });
let request = https.get(url, response => {
const promise = new Promise((x, y) => { fulfill = x; reject = y; });
const options = requestOptions(url);
const request = https.get(options, response => {
if (response.statusCode !== 200) {
let error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`);
const error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`);
// consume response data to free up memory

@@ -205,7 +209,7 @@ response.resume();

}
let file = fs.createWriteStream(destinationPath);
const file = fs.createWriteStream(destinationPath);
file.on('finish', () => fulfill());
file.on('error', error => reject(error));
response.pipe(file);
let totalBytes = parseInt(response.headers['content-length'], 10);
const totalBytes = parseInt(response.headers['content-length'], 10);
if (progressCallback)

@@ -230,1 +234,16 @@ response.on('data', onData.bind(null, totalBytes));

}
function requestOptions(url, method = 'GET') {
const result = URL.parse(url);
result.method = method;
const proxyURL = getProxyForUrl(url);
if (proxyURL) {
const parsedProxyURL = URL.parse(proxyURL);
parsedProxyURL.secureProxy = parsedProxyURL.protocol === 'https:';
result.agent = new ProxyAgent(parsedProxyURL);
}
return result;
}

@@ -24,3 +24,3 @@ /**

this.classes = new Map();
for (let cls of classesArray)
for (const cls of classesArray)
this.classes.set(cls.name, cls);

@@ -42,3 +42,3 @@ }

this.events = new Map();
for (let member of membersArray) {
for (const member of membersArray) {
this.members.set(member.name, member);

@@ -70,3 +70,3 @@ if (member.type === 'method')

this.async = async;
for (let arg of argsArray)
for (const arg of argsArray)
this.args.set(arg.name, arg);

@@ -73,0 +73,0 @@ }

@@ -61,11 +61,11 @@ /**

module.exports = async function lint(page, mdSources, jsSources) {
let mdResult = await mdBuilder(page, mdSources);
let jsResult = await jsBuilder(jsSources);
let jsDocumentation = filterJSDocumentation(jsResult.documentation);
let mdDocumentation = mdResult.documentation;
const mdResult = await mdBuilder(page, mdSources);
const jsResult = await jsBuilder(jsSources);
const jsDocumentation = filterJSDocumentation(jsResult.documentation);
const mdDocumentation = mdResult.documentation;
let jsErrors = jsResult.errors;
const jsErrors = jsResult.errors;
jsErrors.push(...checkDuplicates(jsDocumentation));
let mdErrors = mdResult.errors;
const mdErrors = mdResult.errors;
mdErrors.push(...compareDocumentations(mdDocumentation, jsDocumentation));

@@ -76,3 +76,3 @@ mdErrors.push(...checkDuplicates(mdDocumentation));

// Push all errors with proper prefixes
let errors = jsErrors.map(error => '[JavaScript] ' + error);
const errors = jsErrors.map(error => '[JavaScript] ' + error);
errors.push(...mdErrors.map(error => '[MarkDown] ' + error));

@@ -88,4 +88,4 @@ return errors.map(error => Message.error(error));

const errors = [];
for (let cls of doc.classesArray) {
let members = cls.membersArray;
for (const cls of doc.classesArray) {
const members = cls.membersArray;

@@ -100,3 +100,3 @@ // Events should go first.

// Constructor should be right after events and before all other members.
let constructorIndex = members.findIndex(member => member.type === 'method' && member.name === 'constructor');
const constructorIndex = members.findIndex(member => member.type === 'method' && member.name === 'constructor');
if (constructorIndex > 0 && members[constructorIndex - 1].type !== 'event')

@@ -107,4 +107,4 @@ errors.push(`Constructor of ${cls.name} should go before other methods`);

for (let i = 0; i < members.length - 1; ++i) {
let member1 = cls.membersArray[i];
let member2 = cls.membersArray[i + 1];
const member1 = cls.membersArray[i];
const member2 = cls.membersArray[i + 1];
if (member1.type !== 'event' || member2.type !== 'event')

@@ -118,4 +118,4 @@ continue;

for (let i = 0; i < members.length - 1; ++i) {
let member1 = cls.membersArray[i];
let member2 = cls.membersArray[i + 1];
const member1 = cls.membersArray[i];
const member2 = cls.membersArray[i + 1];
if (member1.type === 'event' || member2.type === 'event')

@@ -145,7 +145,7 @@ continue;

// Filter classes and methods.
let classes = [];
for (let cls of jsDocumentation.classesArray) {
const classes = [];
for (const cls of jsDocumentation.classesArray) {
if (EXCLUDE_CLASSES.has(cls.name))
continue;
let members = cls.membersArray.filter(member => {
const members = cls.membersArray.filter(member => {
if (member.name.startsWith('_'))

@@ -166,15 +166,15 @@ return false;

const errors = [];
let classes = new Set();
const classes = new Set();
// Report duplicates.
for (let cls of doc.classesArray) {
for (const cls of doc.classesArray) {
if (classes.has(cls.name))
errors.push(`Duplicate declaration of class ${cls.name}`);
classes.add(cls.name);
let members = new Set();
for (let member of cls.membersArray) {
const members = new Set();
for (const member of cls.membersArray) {
if (members.has(member.name))
errors.push(`Duplicate declaration of method ${cls.name}.${member.name}()`);
members.add(member.name);
let args = new Set();
for (let arg of member.argsArray) {
const args = new Set();
for (const arg of member.argsArray) {
if (args.has(arg.name))

@@ -199,9 +199,9 @@ errors.push(`Duplicate declaration of argument ${cls.name}.${member.name} "${arg.name}"`);

const expectedClasses = Array.from(expected.classes.keys()).sort();
let classesDiff = diff(actualClasses, expectedClasses);
for (let className of classesDiff.extra)
const classesDiff = diff(actualClasses, expectedClasses);
for (const className of classesDiff.extra)
errors.push(`Non-existing class found: ${className}`);
for (let className of classesDiff.missing)
for (const className of classesDiff.missing)
errors.push(`Class not found: ${className}`);
for (let className of classesDiff.equal) {
for (const className of classesDiff.equal) {
const actualClass = actual.classes.get(className);

@@ -212,8 +212,8 @@ const expectedClass = expected.classes.get(className);

const methodDiff = diff(actualMethods, expectedMethods);
for (let methodName of methodDiff.extra)
for (const methodName of methodDiff.extra)
errors.push(`Non-existing method found: ${className}.${methodName}()`);
for (let methodName of methodDiff.missing)
for (const methodName of methodDiff.missing)
errors.push(`Method not found: ${className}.${methodName}()`);
for (let methodName of methodDiff.equal) {
for (const methodName of methodDiff.equal) {
const actualMethod = actualClass.methods.get(methodName);

@@ -233,6 +233,6 @@ const expectedMethod = expectedClass.methods.get(methodName);

if (argDiff.extra.length || argDiff.missing.length) {
let text = [`Method ${className}.${methodName}() fails to describe its parameters:`];
for (let arg of argDiff.missing)
const text = [`Method ${className}.${methodName}() fails to describe its parameters:`];
for (const arg of argDiff.missing)
text.push(`- Argument not found: ${arg}`);
for (let arg of argDiff.extra)
for (const arg of argDiff.extra)
text.push(`- Non-existing argument found: ${arg}`);

@@ -245,5 +245,5 @@ errors.push(text.join('\n'));

const propertyDiff = diff(actualProperties, expectedProperties);
for (let propertyName of propertyDiff.extra)
for (const propertyName of propertyDiff.extra)
errors.push(`Non-existing property found: ${className}.${propertyName}`);
for (let propertyName of propertyDiff.missing)
for (const propertyName of propertyDiff.missing)
errors.push(`Property not found: ${className}.${propertyName}`);

@@ -254,5 +254,5 @@

const eventsDiff = diff(actualEvents, expectedEvents);
for (let eventName of eventsDiff.extra)
for (const eventName of eventsDiff.extra)
errors.push(`Non-existing event found in class ${className}: '${eventName}'`);
for (let eventName of eventsDiff.missing)
for (const eventName of eventsDiff.missing)
errors.push(`Event not found in class ${className}: '${eventName}'`);

@@ -277,4 +277,4 @@ }

return {extra: actual.slice(), missing: [], equal: []};
let d = new Array(N);
let bt = new Array(N);
const d = new Array(N);
const bt = new Array(N);
for (let i = 0; i < N; ++i) {

@@ -293,3 +293,3 @@ d[i] = new Array(M);

}
let diag = val(i - 1, j - 1);
const diag = val(i - 1, j - 1);
if (actual[i] === expected[j] && d[i][j] < diag + 1) {

@@ -304,5 +304,5 @@ d[i][j] = diag + 1;

let j = M - 1;
let missing = [];
let extra = [];
let equal = [];
const missing = [];
const extra = [];
const equal = [];
while (i >= 0 && j >= 0) {

@@ -309,0 +309,0 @@ switch (bt[i][j]) {

@@ -18,3 +18,3 @@ /**

const esprima = require('esprima');
const ESTreeWalker = require('./ESTreeWalker');
const ESTreeWalker = require('../../ESTreeWalker');
const Documentation = require('./Documentation');

@@ -31,4 +31,4 @@

this._text = text;
let ast = esprima.parseScript(this._text, {loc: true, range: true});
let walker = new ESTreeWalker(node => {
const ast = esprima.parseScript(this._text, {loc: true, range: true});
const walker = new ESTreeWalker(node => {
if (node.type === 'ClassDeclaration')

@@ -54,5 +54,5 @@ this._onClassDeclaration(node);

console.assert(node.value.type === 'FunctionExpression');
let methodName = this._extractText(node.key);
const methodName = this._extractText(node.key);
if (node.kind === 'get') {
let property = Documentation.Member.createProperty(methodName);
const property = Documentation.Member.createProperty(methodName);
this._currentClassMembers.push(property);

@@ -66,3 +66,3 @@ return;

// Extract properties from constructor.
let walker = new ESTreeWalker(node => {
const walker = new ESTreeWalker(node => {
if (node.type !== 'AssignmentExpression')

@@ -78,3 +78,3 @@ return;

} else if (!hasReturn) {
let walker = new ESTreeWalker(node => {
const walker = new ESTreeWalker(node => {
if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')

@@ -88,3 +88,3 @@ return ESTreeWalker.SkipSubtree;

const args = [];
for (let param of node.value.params) {
for (const param of node.value.params) {
if (param.type === 'AssignmentPattern')

@@ -101,3 +101,3 @@ args.push(new Documentation.Argument(param.left.name));

}
let method = Documentation.Member.createMethod(methodName, args, hasReturn, node.value.async);
const method = Documentation.Member.createMethod(methodName, args, hasReturn, node.value.async);
this._currentClassMembers.push(method);

@@ -118,3 +118,3 @@ return ESTreeWalker.SkipSubtree;

}
for (let property of node.right.properties) {
for (const property of node.right.properties) {
if (property.type !== 'Property' || property.key.type !== 'Identifier' || property.value.type !== 'Literal')

@@ -129,3 +129,3 @@ continue;

return;
let jsClass = new Documentation.Class(this._currentClassName, this._currentClassMembers);
const jsClass = new Documentation.Class(this._currentClassName, this._currentClassMembers);
this.classes.push(jsClass);

@@ -138,4 +138,4 @@ this._currentClassName = null;

this.classes = this.classes.map(cls => {
let events = this._eventsByClassName.get(cls.name) || [];
let members = cls.membersArray.concat(events);
const events = this._eventsByClassName.get(cls.name) || [];
const members = cls.membersArray.concat(events);
return new Documentation.Class(cls.name, members);

@@ -148,3 +148,3 @@ });

return null;
let text = this._text.substring(node.range[0], node.range[1]).trim();
const text = this._text.substring(node.range[0], node.range[1]).trim();
return text;

@@ -159,6 +159,6 @@ }

module.exports = async function(sources) {
let classes = [];
let errors = [];
for (let source of sources) {
let outline = new JSOutline(source.text());
const classes = [];
const errors = [];
for (const source of sources) {
const outline = new JSOutline(source.text());
classes.push(...outline.classes);

@@ -165,0 +165,0 @@ errors.push(...outline.errors);

@@ -36,7 +36,7 @@ /**

const {classes, errors} = await page.evaluate(() => {
let classes = [];
const classes = [];
let currentClass = {};
let member = {};
let errors = [];
for (let element of document.body.querySelectorAll('h3, h4, h4 + ul > li')) {
const errors = [];
for (const element of document.body.querySelectorAll('h3, h4, h4 + ul > li')) {
if (element.matches('h3')) {

@@ -86,18 +86,18 @@ currentClass = {

for (const cls of classes) {
let match = cls.name.match(classHeading);
const match = cls.name.match(classHeading);
if (!match)
continue;
currentClassName = match[1];
for (let member of cls.members) {
for (const member of cls.members) {
if (constructorRegex.test(member.name)) {
let match = member.name.match(constructorRegex);
const match = member.name.match(constructorRegex);
handleMethod.call(this, member, match[1], 'constructor', match[2]);
} else if (methodRegex.test(member.name)) {
let match = member.name.match(methodRegex);
const match = member.name.match(methodRegex);
handleMethod.call(this, member, match[1], match[2], match[3]);
} else if (propertyRegex.test(member.name)) {
let match = member.name.match(propertyRegex);
const match = member.name.match(propertyRegex);
handleProperty.call(this, member, match[1], match[2]);
} else if (eventRegex.test(member.name)) {
let match = member.name.match(eventRegex);
const match = member.name.match(eventRegex);
handleEvent.call(this, member, match[1]);

@@ -117,4 +117,4 @@ }

this.errors.push(`Heading arguments for "${member.name}" do not match described ones, i.e. "${parameters}" != "${member.args.join(', ')}"`);
let args = member.args.map(arg => new Documentation.Argument(arg));
let method = Documentation.Member.createMethod(methodName, args, member.hasReturn, false);
const args = member.args.map(arg => new Documentation.Argument(arg));
const method = Documentation.Member.createMethod(methodName, args, member.hasReturn, false);
currentClassMembers.push(method);

@@ -155,6 +155,6 @@ }

module.exports = async function(page, sources) {
let classes = [];
let errors = [];
for (let source of sources) {
let outline = await MDOutline.create(page, source.text());
const classes = [];
const errors = [];
for (const source of sources) {
const outline = await MDOutline.create(page, source.text());
classes.push(...outline.classes);

@@ -161,0 +161,0 @@ errors.push(...outline.errors);

@@ -22,5 +22,5 @@ /**

module.exports = function(sources) {
let messages = [];
const messages = [];
let commands = [];
for (let source of sources) {
for (const source of sources) {
const text = source.text();

@@ -49,6 +49,6 @@ const commandStartRegex = /<!--\s*gen:([a-z]+)(?:\s*\(\s*([^)]*)\s*\))?\s*-->/ig;

let changedSources = new Set();
const changedSources = new Set();
// Iterate commands in reverse order so that edits don't conflict.
commands.sort((a, b) => b.from - a.from);
for (let command of commands) {
for (const command of commands) {
let newText = command.source.text();

@@ -60,3 +60,3 @@ if (command.name === 'version')

}
for (source of changedSources)
for (const source of changedSources)
messages.push(Message.warning(`GEN: updated ${source.projectPath()}`));

@@ -73,3 +73,3 @@ return messages;

// Filter sane commands
let goodCommands = commands.filter(command => {
const goodCommands = commands.filter(command => {
if (command.name === 'version')

@@ -76,0 +76,0 @@ return check(command, !command.arg, `"gen:version" should not have argument`);

@@ -24,4 +24,4 @@ /**

it('should throw for unknown command', function() {
let source = factory.createForTest('doc.md', getCommand('unknownCommand()'));
let messages = preprocessor([source]);
const source = factory.createForTest('doc.md', getCommand('unknownCommand()'));
const messages = preprocessor([source]);
expect(source.hasUpdatedText()).toBe(false);

@@ -34,4 +34,4 @@ expect(messages.length).toBe(1);

it('should work', function() {
let source = factory.createForTest('doc.md', `Puppeteer v${getCommand('version')}`);
let messages = preprocessor([source]);
const source = factory.createForTest('doc.md', `Puppeteer v${getCommand('version')}`);
const messages = preprocessor([source]);
expect(messages.length).toBe(1);

@@ -43,3 +43,3 @@ expect(messages[0].type).toBe('warning');

it('should tolerate different writing', function() {
let source = factory.createForTest('doc.md', `Puppeteer v<!-- gEn:version ( ) -->WHAT
const source = factory.createForTest('doc.md', `Puppeteer v<!-- gEn:version ( ) -->WHAT
<!-- GEN:stop -->`);

@@ -50,4 +50,4 @@ preprocessor([source]);

it('should not tolerate missing gen:stop', function() {
let source = factory.createForTest('doc.md', `<!--GEN:version-->`);
let messages = preprocessor([source]);
const source = factory.createForTest('doc.md', `<!--GEN:version-->`);
const messages = preprocessor([source]);
expect(source.hasUpdatedText()).toBe(false);

@@ -54,0 +54,0 @@ expect(messages.length).toBe(1);

@@ -26,3 +26,3 @@ /**

const warnings = [];
for (let source of sources) {
for (const source of sources) {
const newText = markdownToc.insert(source.text());

@@ -29,0 +29,0 @@ if (source.setText(newText))

@@ -55,3 +55,3 @@ #!/usr/bin/env node

let argv = require('minimist')(process.argv.slice(2), {
const argv = require('minimist')(process.argv.slice(2), {
alias: { u: 'url', h: 'help' },

@@ -65,4 +65,4 @@ });

let url = argv.url || DEVICES_URL;
let outputPath = argv._[0];
const url = argv.url || DEVICES_URL;
const outputPath = argv._[0];
if (!outputPath) {

@@ -77,3 +77,3 @@ console.log('ERROR: output file name is missing. Use --help for help.');

console.log('GET ' + url);
let text = await httpGET(url);
const text = await httpGET(url);
let json = null;

@@ -86,7 +86,7 @@ try {

}
let devicePayloads = json.extensions.filter(extension => extension.type === 'emulated-device').map(extension => extension.device);
const devicePayloads = json.extensions.filter(extension => extension.type === 'emulated-device').map(extension => extension.device);
let devices = [];
for (let payload of devicePayloads) {
let device = createDevice(payload, false);
let landscape = createDevice(payload, true);
for (const payload of devicePayloads) {
const device = createDevice(payload, false);
const landscape = createDevice(payload, true);
devices.push(device);

@@ -99,6 +99,6 @@ if (landscape.viewport.width !== device.viewport.width || landscape.viewport.height !== device.viewport.height)

// Use single-quotes instead of double-quotes to conform with codestyle.
let serialized = JSON.stringify(devices, null, 2)
const serialized = JSON.stringify(devices, null, 2)
.replace(/'/g, `\\'`)
.replace(/"/g, `'`);
let result = util.format(template, serialized);
const result = util.format(template, serialized);
fs.writeFileSync(outputPath, result, 'utf8');

@@ -105,0 +105,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc