├── test
├── assets
│ ├── sw.js
│ ├── empty.html
│ ├── simple.json
│ ├── file-to-upload.txt
│ ├── jscoverage
│ │ ├── script1.js
│ │ ├── script2.js
│ │ ├── unused.html
│ │ ├── ranges.html
│ │ ├── multiple.html
│ │ ├── sourceurl.html
│ │ ├── simple.html
│ │ └── involved.html
│ ├── frames
│ │ ├── script.js
│ │ ├── style.css
│ │ ├── frame.html
│ │ ├── two-frames.html
│ │ └── nested-frames.html
│ ├── csscoverage
│ │ ├── stylesheet1.css
│ │ ├── stylesheet2.css
│ │ ├── media.html
│ │ ├── Dosis-Regular.ttf
│ │ ├── simple.html
│ │ ├── sourceurl.html
│ │ ├── unused.html
│ │ ├── multiple.html
│ │ ├── involved.html
│ │ └── OFL.txt
│ ├── injectedstyle.css
│ ├── one-style.css
│ ├── pptr.png
│ ├── tamperable.html
│ ├── injectedfile.js
│ ├── digits
│ │ ├── 0.png
│ │ ├── 1.png
│ │ ├── 2.png
│ │ ├── 3.png
│ │ ├── 4.png
│ │ ├── 5.png
│ │ ├── 6.png
│ │ ├── 7.png
│ │ ├── 8.png
│ │ └── 9.png
│ ├── mobile.html
│ ├── one-style.html
│ ├── self-request.html
│ ├── input
│ │ ├── fileupload.html
│ │ ├── button.html
│ │ ├── textarea.html
│ │ ├── scrollable.html
│ │ ├── checkbox.html
│ │ ├── touches.html
│ │ ├── keyboard.html
│ │ ├── mouse-helper.js
│ │ └── select.html
│ ├── error.html
│ ├── playground.html
│ ├── networkidle.html
│ ├── shadow.html
│ ├── grid.html
│ └── detect-touch.html
├── golden
│ ├── grid-cell-0.png
│ ├── grid-cell-1.png
│ ├── grid-cell-2.png
│ ├── grid-cell-3.png
│ ├── transparent.png
│ ├── screenshot-sanity.png
│ ├── mock-binary-response.png
│ ├── screenshot-clip-rect.png
│ ├── screenshot-clip-odd-size.png
│ ├── screenshot-grid-fullpage.png
│ ├── screenshot-element-rotate.png
│ ├── screenshot-offscreen-clip.png
│ ├── screenshot-element-bounding-box.png
│ ├── screenshot-element-padding-border.png
│ ├── screenshot-element-scrolled-into-view.png
│ ├── nested-frames.txt
│ ├── reconnect-nested-frames.txt
│ ├── jscoverage-involved.txt
│ └── csscoverage-involved.txt
├── diffstyle.css
├── server
│ ├── run.js
│ ├── cert.pem
│ ├── key.pem
│ └── SimpleServer.js
├── frame-utils.js
└── golden-utils.js
├── utils
├── doclint
│ ├── .gitignore
│ ├── check_public_api
│ │ ├── test
│ │ │ ├── diff-classes
│ │ │ │ ├── foo.js
│ │ │ │ ├── other.js
│ │ │ │ ├── doc.md
│ │ │ │ └── result.txt
│ │ │ ├── .gitignore
│ │ │ ├── diff-properties
│ │ │ │ ├── doc.md
│ │ │ │ ├── result.txt
│ │ │ │ └── foo.js
│ │ │ ├── diff-events
│ │ │ │ ├── doc.md
│ │ │ │ ├── foo.js
│ │ │ │ └── result.txt
│ │ │ ├── check-duplicates
│ │ │ │ ├── foo.js
│ │ │ │ ├── result.txt
│ │ │ │ └── doc.md
│ │ │ ├── diff-methods
│ │ │ │ ├── result.txt
│ │ │ │ ├── doc.md
│ │ │ │ └── foo.js
│ │ │ ├── diff-arguments
│ │ │ │ ├── foo.js
│ │ │ │ ├── doc.md
│ │ │ │ └── result.txt
│ │ │ ├── check-returns
│ │ │ │ ├── doc.md
│ │ │ │ ├── foo.js
│ │ │ │ └── result.txt
│ │ │ ├── check-sorting
│ │ │ │ ├── doc.md
│ │ │ │ ├── foo.js
│ │ │ │ └── result.txt
│ │ │ ├── js-builder-common
│ │ │ │ ├── foo.js
│ │ │ │ └── result.txt
│ │ │ ├── js-builder-inheritance
│ │ │ │ ├── foo.js
│ │ │ │ └── result.txt
│ │ │ ├── md-builder-common
│ │ │ │ ├── doc.md
│ │ │ │ └── result.txt
│ │ │ └── test.js
│ │ ├── Documentation.js
│ │ └── MDBuilder.js
│ ├── README.md
│ ├── toc.js
│ ├── Message.js
│ ├── preprocessor
│ │ ├── test.js
│ │ └── index.js
│ ├── cli.js
│ └── SourceFactory.js
├── apply_next_version.js
├── testrunner
│ ├── index.js
│ ├── examples
│ │ ├── timeout.js
│ │ ├── hookfail.js
│ │ ├── hooktimeout.js
│ │ ├── unhandledpromiserejection.js
│ │ └── fail.js
│ ├── README.md
│ ├── Multimap.js
│ ├── Matchers.js
│ └── Reporter.js
├── node6-transform
│ ├── index.js
│ ├── test
│ │ └── test.js
│ └── TransformAsyncFunctions.js
├── ESTreeWalker.js
└── check_availability.js
├── .eslintignore
├── .editorconfig
├── tsconfig.json
├── .gitignore
├── .npmignore
├── .appveyor.yml
├── lib
├── .eslintrc.js
├── Puppeteer.js
├── externs.d.ts
├── Dialog.js
├── Multimap.js
├── Tracing.js
├── EmulationManager.js
├── NavigatorWatcher.js
├── ElementHandle.js
└── ExecutionContext.js
├── examples
├── screenshot.js
├── screenshot-fullpage.js
├── proxy.js
├── pdf.js
├── block-images.js
├── detect-sniff.js
├── custom-event.js
├── search.js
└── README.md
├── index.js
├── docs
└── issue_template.md
├── .travis.yml
├── package.json
├── .eslintrc.js
├── install.js
└── CONTRIBUTING.md
/test/assets/sw.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/assets/empty.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/doclint/.gitignore:
--------------------------------------------------------------------------------
1 | output/
2 |
--------------------------------------------------------------------------------
/test/assets/simple.json:
--------------------------------------------------------------------------------
1 | {"foo": "bar"}
2 |
--------------------------------------------------------------------------------
/test/assets/file-to-upload.txt:
--------------------------------------------------------------------------------
1 | contents of the file
--------------------------------------------------------------------------------
/test/assets/jscoverage/script1.js:
--------------------------------------------------------------------------------
1 | console.log(3);
2 |
--------------------------------------------------------------------------------
/test/assets/jscoverage/script2.js:
--------------------------------------------------------------------------------
1 | console.log(3);
2 |
--------------------------------------------------------------------------------
/test/assets/frames/script.js:
--------------------------------------------------------------------------------
1 | console.log('Cheers!');
2 |
--------------------------------------------------------------------------------
/test/assets/frames/style.css:
--------------------------------------------------------------------------------
1 | div {
2 | color: blue;
3 | }
4 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/stylesheet1.css:
--------------------------------------------------------------------------------
1 | body {
2 | color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/test/assets/injectedstyle.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: red;
3 | }
4 |
--------------------------------------------------------------------------------
/test/assets/jscoverage/unused.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/assets/one-style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: pink;
3 | }
4 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-classes/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | }
3 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-classes/other.js:
--------------------------------------------------------------------------------
1 | class Other {
2 | }
3 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/stylesheet2.css:
--------------------------------------------------------------------------------
1 | html {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
--------------------------------------------------------------------------------
/test/assets/pptr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/pptr.png
--------------------------------------------------------------------------------
/test/assets/tamperable.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/.gitignore:
--------------------------------------------------------------------------------
1 | result-actual.txt
2 | result-diff.html
3 |
--------------------------------------------------------------------------------
/test/assets/injectedfile.js:
--------------------------------------------------------------------------------
1 | window.__injected = 42;
2 | window.__injectedError = new Error('hi');
--------------------------------------------------------------------------------
/test/assets/digits/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/0.png
--------------------------------------------------------------------------------
/test/assets/digits/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/1.png
--------------------------------------------------------------------------------
/test/assets/digits/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/2.png
--------------------------------------------------------------------------------
/test/assets/digits/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/3.png
--------------------------------------------------------------------------------
/test/assets/digits/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/4.png
--------------------------------------------------------------------------------
/test/assets/digits/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/5.png
--------------------------------------------------------------------------------
/test/assets/digits/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/6.png
--------------------------------------------------------------------------------
/test/assets/digits/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/7.png
--------------------------------------------------------------------------------
/test/assets/digits/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/8.png
--------------------------------------------------------------------------------
/test/assets/digits/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/digits/9.png
--------------------------------------------------------------------------------
/test/assets/jscoverage/ranges.html:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/test/assets/mobile.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/assets/one-style.html:
--------------------------------------------------------------------------------
1 |
2 |
hello, world!
3 |
--------------------------------------------------------------------------------
/test/golden/grid-cell-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/grid-cell-0.png
--------------------------------------------------------------------------------
/test/golden/grid-cell-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/grid-cell-1.png
--------------------------------------------------------------------------------
/test/golden/grid-cell-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/grid-cell-2.png
--------------------------------------------------------------------------------
/test/golden/grid-cell-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/grid-cell-3.png
--------------------------------------------------------------------------------
/test/golden/transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/transparent.png
--------------------------------------------------------------------------------
/test/assets/jscoverage/multiple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/test/assets/jscoverage/sourceurl.html:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/test/golden/screenshot-sanity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-sanity.png
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-properties/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | #### foo.a
4 |
5 | #### foo.c
6 |
--------------------------------------------------------------------------------
/test/assets/jscoverage/simple.html:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-classes/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | ### class: Bar
4 |
5 | ### class: Baz
6 |
--------------------------------------------------------------------------------
/test/golden/mock-binary-response.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/mock-binary-response.png
--------------------------------------------------------------------------------
/test/golden/screenshot-clip-rect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-clip-rect.png
--------------------------------------------------------------------------------
/test/assets/csscoverage/media.html:
--------------------------------------------------------------------------------
1 |
3 | hello, world
4 |
5 |
--------------------------------------------------------------------------------
/test/golden/screenshot-clip-odd-size.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-clip-odd-size.png
--------------------------------------------------------------------------------
/test/golden/screenshot-grid-fullpage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-grid-fullpage.png
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-events/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | #### event: 'start'
4 |
5 | #### event: 'stop'
6 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/Dosis-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/assets/csscoverage/Dosis-Regular.ttf
--------------------------------------------------------------------------------
/test/golden/screenshot-element-rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-element-rotate.png
--------------------------------------------------------------------------------
/test/golden/screenshot-offscreen-clip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-offscreen-clip.png
--------------------------------------------------------------------------------
/test/assets/csscoverage/simple.html:
--------------------------------------------------------------------------------
1 |
5 | hello, world
6 |
7 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/sourceurl.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/test/golden/screenshot-element-bounding-box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-element-bounding-box.png
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-properties/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Non-existing property found: Foo.c
2 | [MarkDown] Property not found: Foo.b
--------------------------------------------------------------------------------
/test/assets/csscoverage/unused.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
--------------------------------------------------------------------------------
/test/golden/screenshot-element-padding-border.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-element-padding-border.png
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-events/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | }
3 |
4 | Foo.Events = {
5 | Start: 'start',
6 | Finish: 'finish',
7 | };
8 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | third_party/*
2 | utils/doclint/check_public_api/test/
3 | utils/testrunner/examples/
4 | node6/*
5 | node6-test/*
6 | node6-testrunner/*
7 |
--------------------------------------------------------------------------------
/test/assets/self-request.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/test/golden/screenshot-element-scrolled-into-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oyyd/puppeteer/master/test/golden/screenshot-element-scrolled-into-view.png
--------------------------------------------------------------------------------
/test/assets/frames/frame.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Hi, I'm frame
4 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-events/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Non-existing event found in class Foo: 'stop'
2 | [MarkDown] Event not found in class Foo: 'finish'
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-duplicates/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | test() {
3 | }
4 |
5 | title(arg) {
6 | }
7 | }
8 |
9 | class Bar {
10 | }
11 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-classes/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Non-existing class found: Bar
2 | [MarkDown] Non-existing class found: Baz
3 | [MarkDown] Class not found: Other
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-properties/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | constructor() {
3 | this.a = 42;
4 | this.b = 'hello';
5 | this.emit('done');
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/test/assets/input/fileupload.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | File upload test
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-methods/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Non-existing method found: Foo.proceed()
2 | [MarkDown] Method not found: Foo.stop()
3 | [MarkDown] Property not found: Foo.zzz
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-methods/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | #### foo.$()
4 |
5 | #### foo.money$$money()
6 |
7 | #### foo.proceed()
8 |
9 | #### foo.start()
10 |
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-arguments/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | foo(arg1, arg3 = {}) {
3 | }
4 |
5 | test(...filePaths) {
6 | }
7 |
8 | bar({visibility}) {
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "allowJs": true,
5 | "checkJs": true,
6 | "target": "es2017"
7 | },
8 | "include": [
9 | "lib"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /test/output
3 | /test/test-user-data-dir*
4 | /.local-chromium/
5 | /.dev_profile*
6 | .DS_Store
7 | *.swp
8 | *.pyc
9 | .vscode
10 | package-lock.json
11 | yarn.lock
12 | /node6
13 |
--------------------------------------------------------------------------------
/test/assets/error.html:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-duplicates/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Duplicate declaration of method Foo.test()
2 | [MarkDown] Duplicate declaration of argument Foo.title "arg"
3 | [MarkDown] Duplicate declaration of class Bar
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-methods/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | start() {
3 | }
4 |
5 | stop() {
6 | }
7 |
8 | get zzz() {
9 | }
10 |
11 | $() {
12 | }
13 |
14 | money$$money() {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test/diffstyle.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: monospace;
3 | white-space: pre;
4 | }
5 |
6 | ins {
7 | background-color: #9cffa0;
8 | text-decoration: none;
9 | }
10 |
11 | del {
12 | background-color: #ff9e9e;
13 | }
14 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-returns/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | #### foo.asyncFunction()
4 |
5 | #### foo.return42()
6 |
7 | #### foo.returnNothing()
8 | - returns: <[number]>
9 |
10 | #### foo.www()
11 | - returns <[string]>
12 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-sorting/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | #### event: 'c'
4 |
5 | #### event: 'a'
6 |
7 | #### foo.aaa()
8 |
9 | #### event: 'b'
10 |
11 | #### foo.ddd
12 |
13 | #### foo.ccc()
14 |
15 | #### foo.bbb()
16 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-duplicates/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Bar
2 |
3 | ### class: Foo
4 |
5 | #### foo.test()
6 |
7 | #### foo.test()
8 |
9 | #### foo.title(arg, arg)
10 | - `arg` <[number]>
11 | - `arg` <[number]>
12 |
13 | ### class: Bar
14 |
15 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-sorting/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | constructor() {
3 | this.ddd = 10;
4 | }
5 |
6 | aaa() {}
7 |
8 | bbb() {}
9 |
10 | ccc() {}
11 | }
12 |
13 | Foo.Events = {
14 | a: 'a',
15 | b: 'b',
16 | c: 'c'
17 | }
18 |
--------------------------------------------------------------------------------
/test/golden/nested-frames.txt:
--------------------------------------------------------------------------------
1 | http://localhost:/frames/nested-frames.html
2 | http://localhost:/frames/two-frames.html
3 | http://localhost:/frames/frame.html
4 | http://localhost:/frames/frame.html
5 | http://localhost:/frames/frame.html
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-arguments/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 | #### foo.bar(options)
3 | - `options` <[Object]>
4 |
5 | #### foo.foo(arg1, arg2)
6 | - `arg1` <[string]>
7 | - `arg2` <[string]>
8 |
9 | #### foo.test(...files)
10 | - `...filePaths` <[string]>
11 |
12 |
--------------------------------------------------------------------------------
/test/assets/frames/two-frames.html:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/test/golden/reconnect-nested-frames.txt:
--------------------------------------------------------------------------------
1 | http://localhost:/frames/nested-frames.html
2 | http://localhost:/frames/two-frames.html
3 | http://localhost:/frames/frame.html
4 | http://localhost:/frames/frame.html
5 | http://localhost:/frames/frame.html
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/diff-arguments/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Heading arguments for "foo.test(...files)" do not match described ones, i.e. "...files" != "...filePaths"
2 | [MarkDown] Method Foo.foo() fails to describe its parameters:
3 | - Argument not found: arg3
4 | - Non-existing argument found: arg2
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # exclude all tests
2 | test
3 | utils/node6-transform
4 |
5 | # repeats from .gitignore
6 | node_modules
7 | .local-chromium
8 | .dev_profile*
9 | .DS_Store
10 | *.swp
11 | *.pyc
12 | .vscode
13 | package-lock.json
14 | /node6/test
15 | /node6/utils
16 | /test
17 | /utils
18 | /docs
19 | yarn.lock
20 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/multiple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/test/assets/jscoverage/involved.html:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/js-builder-common/foo.js:
--------------------------------------------------------------------------------
1 | class A {
2 | constructor(delegate) {
3 | this.property1 = 1;
4 | this._property2 = 2;
5 | }
6 |
7 | get getter() {
8 | return null;
9 | }
10 |
11 | async method(foo, bar) {
12 | }
13 | }
14 |
15 | A.Events = {
16 | AnEvent: 'anevent'
17 | };
18 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/js-builder-inheritance/foo.js:
--------------------------------------------------------------------------------
1 | class A {
2 | constructor() {
3 | }
4 |
5 | foo(a) {
6 | }
7 |
8 | bar() {
9 | }
10 | }
11 |
12 | class B extends A {
13 | bar(override) {
14 | }
15 | }
16 |
17 | B.Events = {
18 | // Event with the same name as a super class method.
19 | foo: 'foo'
20 | };
21 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-returns/foo.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | return42() {
3 | return 42;
4 | }
5 |
6 | returnNothing() {
7 | let e = () => {
8 | return 10;
9 | }
10 | e();
11 | }
12 |
13 | www() {
14 | if (1 === 1)
15 | return 'df';
16 | }
17 |
18 | async asyncFunction() {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-sorting/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] Events should go first. Event 'b' in class Foo breaks order
2 | [MarkDown] Event 'c' in class Foo breaks alphabetic ordering of events
3 | [MarkDown] Bad alphabetic ordering of Foo members: Foo.ddd should go after Foo.ccc()
4 | [MarkDown] Bad alphabetic ordering of Foo members: Foo.ccc() should go after Foo.bbb()
--------------------------------------------------------------------------------
/test/assets/playground.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Playground
5 |
6 |
7 |
8 |
9 | First div
10 |
11 | Second div
12 | Inner span
13 |
14 |
15 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/check-returns/result.txt:
--------------------------------------------------------------------------------
1 | [MarkDown] foo.www() has mistyped 'return' type declaration: expected exactly 'returns: ', found 'returns '.
2 | [MarkDown] Async method Foo.asyncFunction should describe return type Promise
3 | [MarkDown] Method Foo.return42 is missing return type description
4 | [MarkDown] Method Foo.returnNothing has unneeded description of return type
--------------------------------------------------------------------------------
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: "6"
4 | - nodejs_version: "7"
5 |
6 | build: off
7 |
8 | install:
9 | - ps: Install-Product node $env:nodejs_version
10 | - npm install
11 | - if "%nodejs_version%" == "7" (
12 | npm run lint &&
13 | npm run coverage &&
14 | npm run test-doclint
15 | ) else (
16 | npm run unit-node6
17 | )
18 |
--------------------------------------------------------------------------------
/test/assets/input/button.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Button test
5 |
6 |
7 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/test/assets/input/textarea.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Textarea test
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/md-builder-common/doc.md:
--------------------------------------------------------------------------------
1 | ### class: Foo
2 |
3 | This is a class.
4 |
5 | #### event: 'frame'
6 | - <[Frame]>
7 |
8 | This event is dispatched.
9 |
10 | #### foo.$(selector)
11 | - `selector` <[string]> A selector to query page for
12 | - returns: <[Promise]<[ElementHandle]>>
13 |
14 | The method runs document.querySelector.
15 |
16 | #### foo.url
17 | - <[string]>
18 |
19 | Contains the URL of the request.
20 |
--------------------------------------------------------------------------------
/test/assets/networkidle.html:
--------------------------------------------------------------------------------
1 |
20 |
--------------------------------------------------------------------------------
/utils/apply_next_version.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 |
4 | const package = require('../package.json');
5 | let version = package.version;
6 | const dashIndex = version.indexOf('-');
7 | if (dashIndex !== -1)
8 | version = version.substring(0, dashIndex);
9 | version += '-next.' + Date.now();
10 | console.log('Setting version to ' + version);
11 | package.version = version;
12 | fs.writeFileSync(path.join(__dirname, '..', 'package.json'), JSON.stringify(package, undefined, 2) + '\n');
--------------------------------------------------------------------------------
/lib/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": "../.eslintrc.js",
3 | /**
4 | * ESLint rules
5 | *
6 | * All available rules: http://eslint.org/docs/rules/
7 | *
8 | * Rules take the following form:
9 | * "rule-name", [severity, { opts }]
10 | * Severity: 2 == error, 1 == warning, 0 == off.
11 | */
12 | "rules": {
13 | "no-console": [2, { "allow": ["warn", "error", "assert", "timeStamp", "time", "timeEnd"] }],
14 | "no-debugger": 0,
15 | }
16 | };
17 |
--------------------------------------------------------------------------------
/test/assets/shadow.html:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/test/assets/frames/nested-frames.html:
--------------------------------------------------------------------------------
1 |
11 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/involved.html:
--------------------------------------------------------------------------------
1 |
24 | woof!
25 | fancy text
26 |
27 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/md-builder-common/result.txt:
--------------------------------------------------------------------------------
1 | {
2 | "classes": [
3 | {
4 | "name": "Foo",
5 | "members": [
6 | {
7 | "name": "frame",
8 | "type": "event",
9 | "hasReturn": false,
10 | "async": false,
11 | "args": []
12 | },
13 | {
14 | "name": "$",
15 | "type": "method",
16 | "hasReturn": true,
17 | "async": false,
18 | "args": [
19 | "selector"
20 | ]
21 | },
22 | {
23 | "name": "url",
24 | "type": "property",
25 | "hasReturn": false,
26 | "async": false,
27 | "args": []
28 | }
29 | ]
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/test/golden/jscoverage-involved.txt:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "url": "http://localhost:/jscoverage/involved.html",
4 | "ranges": [
5 | {
6 | "start": 0,
7 | "end": 35
8 | },
9 | {
10 | "start": 50,
11 | "end": 100
12 | },
13 | {
14 | "start": 107,
15 | "end": 141
16 | },
17 | {
18 | "start": 148,
19 | "end": 160
20 | },
21 | {
22 | "start": 168,
23 | "end": 207
24 | }
25 | ],
26 | "text": "\nfunction foo() {\n if (1 > 2)\n console.log(1);\n if (1 < 2)\n console.log(2);\n let x = 1 > 2 ? 'foo' : 'bar';\n let y = 1 < 2 ? 'foo' : 'bar';\n let z = () => {};\n let q = () => {};\n q();\n}\n\nfoo();\n"
27 | }
28 | ]
--------------------------------------------------------------------------------
/test/golden/csscoverage-involved.txt:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "url": "http://localhost:/csscoverage/involved.html",
4 | "ranges": [
5 | {
6 | "start": 149,
7 | "end": 297
8 | },
9 | {
10 | "start": 327,
11 | "end": 433
12 | }
13 | ],
14 | "text": "\n@charset \"utf-8\";\n@namespace svg url(http://www.w3.org/2000/svg);\n@font-face {\n font-family: \"Example Font\";\n src: url(\"./Dosis-Regular.ttf\");\n}\n\n#fluffy {\n border: 1px solid black;\n z-index: 1;\n /* -webkit-disabled-property: rgb(1, 2, 3) */\n -lol-cats: \"dogs\" /* non-existing property */\n}\n\n@media (min-width: 1px) {\n span {\n -webkit-border-radius: 10px;\n font-family: \"Example Font\";\n animation: 1s identifier;\n }\n}\n"
15 | }
16 | ]
--------------------------------------------------------------------------------
/utils/doclint/README.md:
--------------------------------------------------------------------------------
1 | # DocLint
2 |
3 | **Doclint** is a small program that lints Puppeteer's documentation against
4 | Puppeteer's source code.
5 |
6 | Doclint works in a few steps:
7 |
8 | 1. Read sources in `lib/` folder, parse AST trees and extract public API
9 | 2. Read sources in `docs/` folder, render markdown to HTML, use puppeteer to traverse the HTML
10 | and extract described API
11 | 3. Compare one API to another
12 |
13 | Doclint is also responsible for general markdown checks, most notably for the table of contents
14 | relevancy.
15 |
16 | ## Running
17 |
18 | ```bash
19 | npm run doc
20 | ```
21 |
22 | ## Tests
23 |
24 | Doclint has its own set of jasmine tests, located at `utils/doclint/test` folder.
25 |
26 | To execute tests, run:
27 |
28 | ```bash
29 | npm run test-doclint
30 | ```
31 |
--------------------------------------------------------------------------------
/test/assets/input/scrollable.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Scrollable test
5 |
6 |
7 |
8 |
22 |
23 |
--------------------------------------------------------------------------------
/utils/testrunner/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const TestRunner = require('./TestRunner');
18 | const Reporter = require('./Reporter');
19 | const Matchers = require('./Matchers');
20 |
21 | module.exports = { TestRunner, Reporter, Matchers };
22 |
--------------------------------------------------------------------------------
/examples/screenshot.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 |
21 | (async() => {
22 | const browser = await puppeteer.launch();
23 | const page = await browser.newPage();
24 | await page.goto('http://example.com');
25 | await page.screenshot({path: 'example.png'});
26 | await browser.close();
27 | })();
28 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | let asyncawait = true;
18 | try {
19 | new Function('async function test(){await 1}');
20 | } catch (error) {
21 | asyncawait = false;
22 | }
23 |
24 | // If node does not support async await, use the compiled version.
25 | if (asyncawait)
26 | module.exports = require('./lib/Puppeteer');
27 | else
28 | module.exports = require('./node6/lib/Puppeteer');
29 |
--------------------------------------------------------------------------------
/test/assets/input/checkbox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Selection Test
5 |
6 |
7 |
8 |
9 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/examples/screenshot-fullpage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 | const devices = require('puppeteer/DeviceDescriptors');
21 |
22 | (async() => {
23 | const browser = await puppeteer.launch();
24 | const page = await browser.newPage();
25 | await page.emulate(devices['iPhone 6']);
26 | await page.goto('https://www.nytimes.com/');
27 | await page.screenshot({path: 'full.png', fullPage: true});
28 | await browser.close();
29 | })();
30 |
--------------------------------------------------------------------------------
/utils/testrunner/examples/timeout.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter} = require('..');
18 |
19 | const runner = new TestRunner({ timeout: 100 });
20 | const reporter = new Reporter(runner);
21 |
22 | const {describe, xdescribe, fdescribe} = runner;
23 | const {it, fit, xit} = runner;
24 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
25 |
26 | describe('testsuite', () => {
27 | it('timeout', async (state) => {
28 | await new Promise(() => {});
29 | });
30 | });
31 |
32 | runner.run();
33 |
--------------------------------------------------------------------------------
/examples/proxy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 |
21 | (async() => {
22 | const browser = await puppeteer.launch({
23 | // Launch chromium using a proxy server on port 9876.
24 | // More on proxying:
25 | // https://www.chromium.org/developers/design-documents/network-settings
26 | args: [ '--proxy-server=127.0.0.1:9876' ]
27 | });
28 | const page = await browser.newPage();
29 | await page.goto('https://google.com');
30 | await browser.close();
31 | })();
32 |
--------------------------------------------------------------------------------
/test/server/run.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | const path = require('path');
17 | const SimpleServer = require('./SimpleServer');
18 |
19 | const port = 8907;
20 | const httpsPort = 8908;
21 | const assetsPath = path.join(__dirname, '..', 'assets');
22 | Promise.all([
23 | SimpleServer.create(assetsPath, port),
24 | SimpleServer.createHTTPS(assetsPath, httpsPort)
25 | ]).then(([server, httpsServer]) => {
26 | console.log(`HTTP: server is running on http://localhost:${port}`);
27 | console.log(`HTTPS: server is running on https://localhost:${httpsPort}`);
28 | });
29 |
--------------------------------------------------------------------------------
/docs/issue_template.md:
--------------------------------------------------------------------------------
1 |
18 |
19 | ### Steps to reproduce
20 |
21 | **Tell us about your environment:**
22 |
23 | * Puppeteer version:
24 | * Platform / OS version:
25 | * URLs (if applicable):
26 | * Node.js version:
27 |
28 | **What steps will reproduce the problem?**
29 |
30 | _Please include code that reproduces the issue._
31 |
32 | 1.
33 | 2.
34 | 3.
35 |
36 | **What is the expected result?**
37 |
38 |
39 | **What happens instead?**
40 |
41 |
--------------------------------------------------------------------------------
/examples/pdf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 |
21 | (async() => {
22 | const browser = await puppeteer.launch();
23 | const page = await browser.newPage();
24 | await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
25 | // page.pdf() is currently supported only in headless mode.
26 | // @see https://bugs.chromium.org/p/chromium/issues/detail?id=753118
27 | await page.pdf({
28 | path: 'hn.pdf',
29 | format: 'letter'
30 | });
31 |
32 | await browser.close();
33 | })();
34 |
--------------------------------------------------------------------------------
/utils/testrunner/examples/hookfail.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter, Matchers} = require('..');
18 |
19 | const runner = new TestRunner();
20 | const reporter = new Reporter(runner);
21 | const {expect} = new Matchers();
22 |
23 | const {describe, xdescribe, fdescribe} = runner;
24 | const {it, fit, xit} = runner;
25 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
26 |
27 | describe('testsuite', () => {
28 | beforeAll(() => {
29 | expect(false).toBeTruthy();
30 | });
31 | it('test', async () => {
32 | });
33 | });
34 |
35 | runner.run();
36 |
--------------------------------------------------------------------------------
/utils/doclint/toc.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const markdownToc = require('markdown-toc');
18 | const Message = require('./Message');
19 |
20 | /**
21 | * @param {!Array} sources
22 | * @return {!Array}
23 | */
24 | module.exports = function(sources) {
25 | const warnings = [];
26 | for (const source of sources) {
27 | const newText = markdownToc.insert(source.text());
28 | if (source.setText(newText))
29 | warnings.push('Regenerated table-of-contexts: ' + source.projectPath());
30 | }
31 | return warnings.map(warning => Message.warning(warning));
32 | };
33 |
--------------------------------------------------------------------------------
/test/assets/input/touches.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Touch test
5 |
6 |
7 |
8 |
9 |
34 |
35 |
--------------------------------------------------------------------------------
/utils/doclint/Message.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class Message {
18 | /**
19 | * @param {string} type
20 | * @param {string} text
21 | */
22 | constructor(type, text) {
23 | this.type = type;
24 | this.text = text;
25 | }
26 |
27 | /**
28 | * @param {string} text
29 | * @return {!Message}
30 | */
31 | static error(text) {
32 | return new Message('error', text);
33 | }
34 |
35 | /**
36 | * @param {string} text
37 | * @return {!Message}
38 | */
39 | static warning(text) {
40 | return new Message('warning', text);
41 | }
42 | }
43 |
44 | module.exports = Message;
45 |
--------------------------------------------------------------------------------
/utils/testrunner/examples/hooktimeout.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter, Matchers} = require('..');
18 |
19 | const runner = new TestRunner({ timeout: 100 });
20 | const reporter = new Reporter(runner);
21 | const {expect} = new Matchers();
22 |
23 | const {describe, xdescribe, fdescribe} = runner;
24 | const {it, fit, xit} = runner;
25 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
26 |
27 | describe('testsuite', () => {
28 | beforeAll(async () => {
29 | await new Promise(() => {});
30 | });
31 | it('something', async (state) => {
32 | });
33 | });
34 |
35 | runner.run();
36 |
--------------------------------------------------------------------------------
/utils/testrunner/examples/unhandledpromiserejection.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter} = require('..');
18 |
19 | const runner = new TestRunner();
20 | const reporter = new Reporter(runner);
21 |
22 | const {describe, xdescribe, fdescribe} = runner;
23 | const {it, fit, xit} = runner;
24 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
25 |
26 | describe('testsuite', () => {
27 | it('failure', async (state) => {
28 | Promise.reject(new Error('fail!'));
29 | });
30 | it('slow', async () => {
31 | await new Promise(x => setTimeout(x, 1000));
32 | });
33 | });
34 |
35 | runner.run();
36 |
--------------------------------------------------------------------------------
/examples/block-images.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc., PhantomJS Authors All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 |
21 | (async() => {
22 | const browser = await puppeteer.launch();
23 | const page = await browser.newPage();
24 | await page.setRequestInterception(true);
25 | page.on('request', request => {
26 | if (request.resourceType() === 'image')
27 | request.abort();
28 | else
29 | request.continue();
30 | });
31 | await page.goto('https://news.google.com/news/');
32 | await page.screenshot({path: 'news.png', fullPage: true});
33 |
34 | await browser.close();
35 | })();
36 |
37 |
--------------------------------------------------------------------------------
/test/assets/grid.html:
--------------------------------------------------------------------------------
1 |
28 |
29 |
49 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/js-builder-common/result.txt:
--------------------------------------------------------------------------------
1 | {
2 | "classes": [
3 | {
4 | "name": "A",
5 | "members": [
6 | {
7 | "name": "property1",
8 | "type": "property",
9 | "hasReturn": false,
10 | "async": false,
11 | "args": []
12 | },
13 | {
14 | "name": "_property2",
15 | "type": "property",
16 | "hasReturn": false,
17 | "async": false,
18 | "args": []
19 | },
20 | {
21 | "name": "constructor",
22 | "type": "method",
23 | "hasReturn": false,
24 | "async": false,
25 | "args": [
26 | "delegate"
27 | ]
28 | },
29 | {
30 | "name": "getter",
31 | "type": "property",
32 | "hasReturn": false,
33 | "async": false,
34 | "args": []
35 | },
36 | {
37 | "name": "method",
38 | "type": "method",
39 | "hasReturn": true,
40 | "async": true,
41 | "args": [
42 | "foo",
43 | "bar"
44 | ]
45 | },
46 | {
47 | "name": "anevent",
48 | "type": "event",
49 | "hasReturn": false,
50 | "async": false,
51 | "args": []
52 | }
53 | ]
54 | }
55 | ]
56 | }
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/js-builder-inheritance/result.txt:
--------------------------------------------------------------------------------
1 | {
2 | "classes": [
3 | {
4 | "name": "A",
5 | "members": [
6 | {
7 | "name": "constructor",
8 | "type": "method",
9 | "hasReturn": false,
10 | "async": false,
11 | "args": []
12 | },
13 | {
14 | "name": "foo",
15 | "type": "method",
16 | "hasReturn": false,
17 | "async": false,
18 | "args": [
19 | "a"
20 | ]
21 | },
22 | {
23 | "name": "bar",
24 | "type": "method",
25 | "hasReturn": false,
26 | "async": false,
27 | "args": []
28 | }
29 | ]
30 | },
31 | {
32 | "name": "B",
33 | "members": [
34 | {
35 | "name": "bar",
36 | "type": "method",
37 | "hasReturn": false,
38 | "async": false,
39 | "args": [
40 | "override"
41 | ]
42 | },
43 | {
44 | "name": "foo",
45 | "type": "event",
46 | "hasReturn": false,
47 | "async": false,
48 | "args": []
49 | },
50 | {
51 | "name": "foo",
52 | "type": "method",
53 | "hasReturn": false,
54 | "async": false,
55 | "args": [
56 | "a"
57 | ]
58 | }
59 | ]
60 | }
61 | ]
62 | }
--------------------------------------------------------------------------------
/test/assets/input/keyboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Keyboard test
5 |
6 |
7 |
8 |
43 |
44 |
--------------------------------------------------------------------------------
/utils/testrunner/README.md:
--------------------------------------------------------------------------------
1 | # TestRunner
2 |
3 | - testrunner is a library: no additional binary required; tests are `node.js` scripts
4 | - parallel wrt IO operations
5 | - supports async/await
6 | - modular
7 | - well-isolated state per execution thread
8 |
9 | Example
10 |
11 | ```js
12 | const {TestRunner, Reporter, Matchers} = require('../utils/testrunner');
13 |
14 | // Runner holds and runs all the tests
15 | const runner = new TestRunner({
16 | parallel: 2, // run 2 parallel threads
17 | timeout: 1000, // setup timeout of 1 second per test
18 | });
19 | // Simple expect-like matchers
20 | const {expect} = new Matchers();
21 |
22 | // Extract jasmine-like DSL into the global namespace
23 | const {describe, xdescribe, fdescribe} = runner;
24 | const {it, fit, xit} = runner;
25 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
26 |
27 | beforeAll(state => {
28 | state.parallelIndex; // either 0 or 1 in this example, depending on the executing thread
29 | state.foo = 'bar'; // set state for every test
30 | });
31 |
32 | describe('math', () => {
33 | it('to be sane', async (state, test) => {
34 | state.parallel; // Very first test will always be ran by the 0's thread
35 | state.foo; // this will be 'bar'
36 | expect(2 + 2).toBe(4);
37 | });
38 | });
39 |
40 | // Reporter subscribes to TestRunner events and displays information in terminal
41 | const reporter = new Reporter(runner);
42 |
43 | // Run all tests.
44 | runner.run();
45 | ```
46 |
--------------------------------------------------------------------------------
/lib/Puppeteer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | const {helper} = require('./helper');
17 | const Launcher = require('./Launcher');
18 |
19 | class Puppeteer {
20 | /**
21 | * @param {!Object=} options
22 | * @return {!Promise}
23 | */
24 | static launch(options) {
25 | return Launcher.launch(options);
26 | }
27 |
28 | /**
29 | * @param {{browserWSEndpoint: string, ignoreHTTPSErrors: boolean}} options
30 | * @return {!Promise}
31 | */
32 | static connect(options) {
33 | return Launcher.connect(options);
34 | }
35 |
36 | /**
37 | * @return {string}
38 | */
39 | static executablePath() {
40 | return Launcher.executablePath();
41 | }
42 |
43 | /**
44 | * @return {!Array}
45 | */
46 | static defaultArgs() {
47 | return Launcher.defaultArgs();
48 | }
49 | }
50 |
51 | module.exports = Puppeteer;
52 | helper.tracePublicAPI(Puppeteer);
53 |
--------------------------------------------------------------------------------
/examples/detect-sniff.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc., PhantomJS Authors All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 |
21 | function sniffDetector() {
22 | const userAgent = window.navigator.userAgent;
23 | const platform = window.navigator.platform;
24 |
25 | window.navigator.__defineGetter__('userAgent', function() {
26 | window.navigator.sniffed = true;
27 | return userAgent;
28 | });
29 |
30 | window.navigator.__defineGetter__('platform', function() {
31 | window.navigator.sniffed = true;
32 | return platform;
33 | });
34 | }
35 |
36 | (async() => {
37 | const browser = await puppeteer.launch();
38 | const page = await browser.newPage();
39 | await page.evaluateOnNewDocument(sniffDetector);
40 | await page.goto('https://www.google.com', {waitUntil: 'networkidle2'});
41 | console.log('Sniffed: ' + (await page.evaluate(() => !!navigator.sniffed)));
42 |
43 | await browser.close();
44 | })();
45 |
--------------------------------------------------------------------------------
/lib/externs.d.ts:
--------------------------------------------------------------------------------
1 | import { Connection as RealConnection, CDPSession as RealCDPSession } from './Connection.js';
2 | import {Browser as RealBrowser, TaskQueue as RealTaskQueue, Target as RealTarget} from './Browser.js';
3 | import * as RealPage from './Page.js';
4 | import {Mouse as RealMouse, Keyboard as RealKeyboard, Touchscreen as RealTouchscreen} from './Input.js';
5 | import {Frame as RealFrame, FrameManager as RealFrameManager} from './FrameManager.js';
6 | import {JSHandle as RealJSHandle, ExecutionContext as RealExecutionContext} from './ExecutionContext.js';
7 | import * as RealElementHandle from './ElementHandle.js';
8 | import * as RealNetworkManager from './NetworkManager.js';
9 | import * as child_process from 'child_process';
10 | export as namespace Puppeteer;
11 |
12 | export class Connection extends RealConnection {}
13 | export class CDPSession extends RealCDPSession {}
14 | export class Mouse extends RealMouse {}
15 | export class Keyboard extends RealKeyboard {}
16 | export class Touchscreen extends RealTouchscreen {}
17 | export class TaskQueue extends RealTaskQueue {}
18 | export class Browser extends RealBrowser {}
19 | export class Target extends RealTarget {}
20 | export class Frame extends RealFrame {}
21 | export class FrameManager extends RealFrameManager {}
22 | export class NetworkManager extends RealNetworkManager {}
23 | export class ElementHandle extends RealElementHandle {}
24 | export class JSHandle extends RealJSHandle {}
25 | export class ExecutionContext extends RealExecutionContext {}
26 | export class Page extends RealPage {}
27 |
28 |
29 | export interface ChildProcess extends child_process.ChildProcess {}
30 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | dist: trusty
3 | addons:
4 | apt:
5 | packages:
6 | # This is required to run new chrome on old trusty
7 | - libnss3
8 | cache:
9 | directories:
10 | - node_modules
11 | # allow headful tests
12 | before_install:
13 | - "export DISPLAY=:99.0"
14 | - "sh -e /etc/init.d/xvfb start"
15 | script:
16 | - 'if [ "$NODE7" = "true" ]; then npm run lint; fi'
17 | - 'if [ "$NODE7" = "true" ]; then npm run coverage; fi'
18 | - 'if [ "$NODE7" = "true" ]; then npm run test-doclint; fi'
19 | - 'if [ "$NODE6" = "true" ]; then npm run unit-node6; fi'
20 | jobs:
21 | include:
22 | - node_js: "7.6.0"
23 | env: NODE7=true
24 | - node_js: "6.4.0"
25 | env: NODE6=true
26 | before_deploy: "npm run apply-next-version"
27 | deploy:
28 | provider: npm
29 | email: aslushnikov@gmail.com
30 | api_key:
31 | secure: S/cwhamxKh0dsI46jewA6AkFXhDQWqfGkEe19WP3uoBg69jBqxs3DMcqGmd7og8RgqFUhhnBon8cDyJD25XHhDSSVANY9sFhwAPsty+Pjc9NHlG9My59tzpKSuhxOHxUaUd4Ug1gjYKTRNh4yYQKuGFm1mS3X34AbHg+eAFz6gVk3oVoc8Uu/rYH/dTP/ZGa29U7tn8DrEiyxoxv1pIEoX66AmAA649+nULwZw1MlhTaZZgYYIHlMC91dY6N6ql3ESj0zZSmQmrdZbWcGkTZDfBmdHMfFhnX9n0D7AgNvKNL+LDBcu7yiGO3hM2BrVOlGv24FFaHF5cLU4wuRuBvRG8cx3j1rG5w8c8jkEN1iL6qcmo76++YfILi3DIzD2n4RAsKeGILTcQOHhY4wqViuy0bb5zc5i/pqbtUERb3ngCZbNYTY8MQ5ertckmEw8daS/irREZfmThY90EPitsYw39f1+tdm9YUpWz/q2MpjqNByk6ycr17i2zFdqy4j/5CwrlmtewCw7Np8ubNITuqL7rst5GojlJvKSA3onuGyJLtSshUFn6lwwAHpkptZ5JWDwkxHiW+ofQ50Td6+NouJrSobOd1JzJ5om4eH2V1hkD5RP5saeDgojbRTMX+j8lQaQAnYQLYj0C2I1i6yl+VP0HUt7L//3zTRUetNTzGdA4=
32 | on:
33 | branch: master
34 | condition: "$NODE7 = true"
35 | skip_cleanup: true
36 | tag: next
37 |
--------------------------------------------------------------------------------
/examples/custom-event.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | 'use strict';
18 |
19 | const puppeteer = require('puppeteer');
20 |
21 | (async() => {
22 | const browser = await puppeteer.launch();
23 | const page = await browser.newPage();
24 |
25 | // Define a window.onCustomEvent function on the page.
26 | await page.exposeFunction('onCustomEvent', e => {
27 | console.log(`${e.type} fired`, e.detail || '');
28 | });
29 |
30 | /**
31 | * Attach an event listener to page to capture a custom event on page load/navigation.
32 | * @param {string} type Event name.
33 | * @return {!Promise}
34 | */
35 | function listenFor(type) {
36 | return page.evaluateOnNewDocument(type => {
37 | document.addEventListener(type, e => {
38 | window.onCustomEvent({type, detail: e.detail});
39 | });
40 | }, type);
41 | }
42 |
43 | await listenFor('app-ready'); // Listen for "app-ready" custom event on page load.
44 |
45 | await page.goto('https://www.chromestatus.com/features', {waitUntil: 'networkidle2'});
46 |
47 | await browser.close();
48 | })();
49 |
--------------------------------------------------------------------------------
/utils/testrunner/examples/fail.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {TestRunner, Reporter, Matchers} = require('..');
18 |
19 | const runner = new TestRunner();
20 | const reporter = new Reporter(runner);
21 | const {expect} = new Matchers();
22 |
23 | const {describe, xdescribe, fdescribe} = runner;
24 | const {it, fit, xit} = runner;
25 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
26 |
27 | describe('testsuite', () => {
28 | it('toBe', async (state) => {
29 | expect(2 + 2).toBe(5);
30 | });
31 | it('toBeFalsy', async (state) => {
32 | expect(true).toBeFalsy();
33 | });
34 | it('toBeTruthy', async (state) => {
35 | expect(false).toBeTruthy();
36 | });
37 | it('toBeGreaterThan', async (state) => {
38 | expect(2).toBeGreaterThan(3);
39 | });
40 | it('toBeNull', async (state) => {
41 | expect(2).toBeNull();
42 | });
43 | it('toContain', async (state) => {
44 | expect('asdf').toContain('e');
45 | });
46 | it('toEqual', async (state) => {
47 | expect([1,2,3]).toEqual([1,2,3,4]);
48 | });
49 | });
50 |
51 | runner.run();
52 |
--------------------------------------------------------------------------------
/test/assets/input/mouse-helper.js:
--------------------------------------------------------------------------------
1 | // This injects a box into the page that moves with the mouse;
2 | // Useful for debugging
3 | (function(){
4 | const box = document.createElement('div');
5 | box.classList.add('mouse-helper');
6 | const styleElement = document.createElement('style');
7 | styleElement.innerHTML = `
8 | .mouse-helper {
9 | pointer-events: none;
10 | position: absolute;
11 | top: 0;
12 | left: 0;
13 | width: 20px;
14 | height: 20px;
15 | background: rgba(0,0,0,.4);
16 | border: 1px solid white;
17 | border-radius: 10px;
18 | margin-left: -10px;
19 | margin-top: -10px;
20 | transition: background .2s, border-radius .2s, border-color .2s;
21 | }
22 | .mouse-helper.button-1 {
23 | transition: none;
24 | background: rgba(0,0,0,0.9);
25 | }
26 | .mouse-helper.button-2 {
27 | transition: none;
28 | border-color: rgba(0,0,255,0.9);
29 | }
30 | .mouse-helper.button-3 {
31 | transition: none;
32 | border-radius: 4px;
33 | }
34 | .mouse-helper.button-4 {
35 | transition: none;
36 | border-color: rgba(255,0,0,0.9);
37 | }
38 | .mouse-helper.button-5 {
39 | transition: none;
40 | border-color: rgba(0,255,0,0.9);
41 | }
42 | `;
43 | document.head.appendChild(styleElement);
44 | document.body.appendChild(box);
45 | document.addEventListener('mousemove', event => {
46 | box.style.left = event.pageX + 'px';
47 | box.style.top = event.pageY + 'px';
48 | updateButtons(event.buttons);
49 | }, true);
50 | document.addEventListener('mousedown', event => {
51 | updateButtons(event.buttons);
52 | box.classList.add('button-' + event.which);
53 | }, true);
54 | document.addEventListener('mouseup', event => {
55 | updateButtons(event.buttons);
56 | box.classList.remove('button-' + event.which);
57 | }, true);
58 | function updateButtons(buttons) {
59 | for (let i = 0; i < 5; i++)
60 | box.classList.toggle('button-' + i, buttons & (1 << i));
61 | }
62 | })();
63 |
--------------------------------------------------------------------------------
/test/server/cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFXzCCA0egAwIBAgIJAM+8uXXn61zZMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
4 | aWRnaXRzIFB0eSBMdGQwIBcNMTcwNzEwMjI1MDA2WhgPMzAxNjExMTAyMjUwMDZa
5 | MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ
6 | bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
7 | ggIKAoICAQCfsIbw1Q91wooUdSu5tDiyrSYB6ZQmY49Y141KQ0ll0ZXzf1sPTpPg
8 | OuBjJE8Fre2Wn3jJ0SfLFyQBMvE49hqWyY/U5Xc367ujqKFQVoItnoV5MM2TPu5J
9 | /zhtf26Vq0Pcrujt5LfRe1JSYKdJ21Tquqa0MAGI0HghaCdSdtyo2xuotnukirKb
10 | QrvP/YNa+ONZT6KW8MFAwfoCOJMo1idrkBA1Wve5xcCd7J9Oy5mWCBxTSR67W2vQ
11 | izoOTSkzD0xoXpFF5V/34zJGWU9t6Z5qytV/w5ROY3Tk9EaKs0NcQmXlCxSmkXil
12 | KSTlZ/VDeDliI92jGn4hT+apisglm3aaTnVVAP0EbZ/CF9Fwb601M7IcAP9ejaeB
13 | EEs+smXpuzhAfxPa5SpZCWeaXLcFq6Ewi2LXrMaChWvbu9AUi3QjuT3u9PW3B0w5
14 | C54FLfvcy9X9dQQ/jCgydF3eyhiO3SuLZqrhofHUn53z4UCEYgbC7uQSv08ep2UD
15 | kT2ARN6aetXVgiQBYS8hcGaFrdsUTSAmT0flt0g8ZoHn+NmuAWxbAx8UnPd0p/EP
16 | B4cZByDOUDGgDMSOEluheiCFlZBQEJnvOhim6rwSje87EzQazGkwRpOrBtzGIGcM
17 | xmotG9IrMbzKb4Z+yg5pOEW2WKEy3f2h8P2bisbbHwYa/tgTHVWbGwIDAQABo1Aw
18 | TjAdBgNVHQ4EFgQUZvIOJVkyQTAd0fpUkpqgcXVID+4wHwYDVR0jBBgwFoAUZvIO
19 | JVkyQTAd0fpUkpqgcXVID+4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
20 | AgEASmqzfr4TOVFbNo3r85QlCQB8W4xaArRpY6k7ugxJm2/9iyyovMKMhCcRvmPV
21 | HZXDTV8WEbLDg3uNF56WU39YmtfjghnsYcGQ/UhnH2PoHQxGIKzOoalLew7kP0Uc
22 | HCmXi7OPjqeFQgjWVhLkL/RIzZ3vOVFQfqodTVIdYDe0O1cNRD0U0rtGa0MHzZlD
23 | y/ZaksiB0E3UL/GsgyJoCxCAF1E80PU9aw1K9COyTOChpUKnhSHC964KlhnJKyOe
24 | HKCYtb5RVrF7ryBcGneLTcQm/oNUp8aRyiwyQIDH7djFSp11MakXBF+EeGR523pp
25 | u+sleR7ZFBGyb3Z5K9NdRdUk0SWu7Hu4jQtJHn+PmIZ1qjfbPv5qDfVd1vmFwqsu
26 | 7NfsLoNm0dQNu5WOMLISQHmQiT86AH2wWQ3l5Sm+g8/BdNLQLklhtZcRhp2efyiL
27 | ciUmGugKqoX+nPIZ36kuoRTZy++AnTiid011vZFe1qrfug/ykWiqWmBSvD/cfRU4
28 | ydoK87cfjIixqmpRZ7j2q+/cDK2SbYN0t/Xrufw3L6TjDgUEL7ZCImcwqqWJz9I8
29 | ASnnL5PhX8bbsUrtE21Ugqk2zYnVnqRO5FjINtlTb07y9pGC/QpBkb1AasF5okhe
30 | 7lw/sMiryKKzS4A10nRV/+gErDBsIBj+cpGPM8brLfZuy2o=
31 | -----END CERTIFICATE-----
32 |
--------------------------------------------------------------------------------
/examples/search.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * @fileoverview Search developers.google.com/web for articles tagged
19 | * "Headless Chrome" and scrape results from the results page.
20 | */
21 |
22 | 'use strict';
23 |
24 | const puppeteer = require('puppeteer');
25 |
26 | (async() => {
27 | const browser = await puppeteer.launch();
28 | const page = await browser.newPage();
29 |
30 | await page.goto('https://developers.google.com/web/');
31 |
32 | // Type into search box.
33 | await page.type('#searchbox input', 'Headless Chrome');
34 |
35 | // Wait for suggest overlay to appear and click "show all results".
36 | const allResultsSelector = '.devsite-suggest-all-results';
37 | await page.waitForSelector(allResultsSelector);
38 | await page.click(allResultsSelector);
39 |
40 | // Wait for the results page to load and display the results.
41 | const resultsSelector = '.gsc-results .gsc-thumbnail-inside a.gs-title';
42 | await page.waitForSelector(resultsSelector);
43 |
44 | // Extract the results from the page.
45 | const links = await page.evaluate(resultsSelector => {
46 | const anchors = Array.from(document.querySelectorAll(resultsSelector));
47 | return anchors.map(anchor => {
48 | const title = anchor.textContent.split('|')[0].trim();
49 | return `${title} - ${anchor.href}`;
50 | });
51 | }, resultsSelector);
52 | console.log(links.join('\n'));
53 |
54 | await browser.close();
55 | })();
56 |
--------------------------------------------------------------------------------
/utils/node6-transform/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const fs = require('fs');
18 | const path = require('path');
19 | const removeRecursive = require('rimraf').sync;
20 | const transformAsyncFunctions = require('./TransformAsyncFunctions');
21 |
22 | const root = path.join(__dirname, '..', '..');
23 | const dest = path.join(__dirname, '..', '..', 'node6');
24 |
25 | if (fs.existsSync(dest))
26 | removeRecursive(dest);
27 | fs.mkdirSync(dest);
28 | fs.mkdirSync(path.join(dest, 'utils'));
29 |
30 | copyFolder(path.join(root, 'lib'), path.join(dest, 'lib'));
31 | copyFolder(path.join(root, 'test'), path.join(dest, 'test'));
32 | copyFolder(path.join(root, 'utils'), path.join(dest, 'utils'));
33 |
34 | function copyFolder(source, target) {
35 | if (fs.existsSync(target))
36 | removeRecursive(target);
37 | fs.mkdirSync(target);
38 |
39 | fs.readdirSync(source).forEach(file => {
40 | const from = path.join(source, file);
41 | const to = path.join(target, file);
42 | if (fs.lstatSync(from).isDirectory())
43 | copyFolder(from, to);
44 | else
45 | copyFile(from, to);
46 | });
47 | }
48 |
49 | function copyFile(from, to) {
50 | let text = fs.readFileSync(from);
51 | if (from.endsWith('.js')) {
52 | text = text.toString();
53 | const prefix = text.startsWith('#!') ? text.substring(0, text.indexOf('\n')) : '';
54 | text = prefix + transformAsyncFunctions(text.substring(prefix.length));
55 | }
56 | fs.writeFileSync(to, text);
57 | }
58 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Running the examples
2 |
3 | Assuming you have a checkout of the Puppeteer repo and have run npm i (or yarn) to install the dependencies, the examples can be run from the root folder like so:
4 |
5 | ```sh
6 | NODE_PATH=../ node examples/search.js
7 | ```
8 |
9 | # Tips & Tricks
10 |
11 | ### Load a Chrome extension
12 |
13 | By default, Puppeteer disables extensions when launching Chrome. You can load a specific
14 | extension using:
15 |
16 | ```js
17 | const browser = await puppeteer.launch({
18 | headless: false,
19 | args: [
20 | '--disable-extensions-except=/path/to/extension/',
21 | '--load-extension=/path/to/extension/',
22 | ]
23 | });
24 | ```
25 |
26 | # Other resources
27 |
28 | > Other useful tools, articles, and projects that use Puppeteer.
29 |
30 | ## Rendering and web scraping
31 | - [Puppetron](https://github.com/cheeaun/puppetron) - Demo site that shows how to use Puppeteer and Headless Chrome to render pages. Inspired by [GoogleChrome/rendertron](https://github.com/GoogleChrome/rendertron).
32 | - [Thal](https://medium.com/@e_mad_ehsan/getting-started-with-puppeteer-and-chrome-headless-for-web-scrapping-6bf5979dee3e "An article on medium") - Getting started with Puppeteer and Chrome Headless for Web Scraping.
33 | - [pupperender](https://github.com/LasaleFamine/pupperender) - Express middleware that checks the User-Agent header of incoming requests, and if it matches one of a configurable set of bots, render the page using Puppeteer. Useful for PWA rendering.
34 | - [headless-chrome-crawler](https://github.com/yujiosaka/headless-chrome-crawler) - Crawler that provides simple APIs to manipulate Headless Chrome and allows you to crawl dynamic websites.
35 | - [puppeteer-examples](https://github.com/checkly/puppeteer-examples) - Puppeteer Headless Chrome examples for real life use cases such as getting useful info from the web pages or common login scenarios.
36 |
37 | ## Testing
38 | - [angular-puppeteer-demo](https://github.com/Quramy/angular-puppeteer-demo) - Demo repository explaining how to use Puppeteer in Karma.
39 | - [mocha-headless-chrome](https://github.com/direct-adv-interfaces/mocha-headless-chrome) - Tool which runs client-side **mocha** tests in the command line through headless Chrome.
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "puppeteer",
3 | "version": "1.0.0-post",
4 | "description": "A high-level API to control headless Chrome over the DevTools Protocol",
5 | "main": "index.js",
6 | "repository": "github:GoogleChrome/puppeteer",
7 | "engines": {
8 | "node": ">=6.4.0"
9 | },
10 | "scripts": {
11 | "unit": "node test/test.js",
12 | "debug-unit": "node --inspect-brk test/test.js",
13 | "test-doclint": "node utils/doclint/check_public_api/test/test.js && node utils/doclint/preprocessor/test.js",
14 | "test": "npm run lint --silent && npm run coverage && npm run test-doclint && npm run test-node6-transformer",
15 | "install": "node install.js",
16 | "lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc",
17 | "doc": "node utils/doclint/cli.js",
18 | "coverage": "cross-env COVERAGE=true npm run unit",
19 | "test-node6-transformer": "node utils/node6-transform/test/test.js",
20 | "build": "node utils/node6-transform/index.js",
21 | "unit-node6": "node node6/test/test.js",
22 | "tsc": "tsc -p .",
23 | "prepublishOnly": "npm run build",
24 | "apply-next-version": "node utils/apply_next_version.js"
25 | },
26 | "author": "The Chromium Authors",
27 | "license": "Apache-2.0",
28 | "dependencies": {
29 | "debug": "^2.6.8",
30 | "extract-zip": "^1.6.5",
31 | "https-proxy-agent": "^2.1.0",
32 | "mime": "^1.3.4",
33 | "progress": "^2.0.0",
34 | "proxy-from-env": "^1.0.0",
35 | "rimraf": "^2.6.1",
36 | "ws": "^3.0.0"
37 | },
38 | "puppeteer": {
39 | "chromium_revision": "526987"
40 | },
41 | "devDependencies": {
42 | "@types/debug": "0.0.30",
43 | "@types/extract-zip": "^1.6.2",
44 | "@types/mime": "^1.3.1",
45 | "@types/node": "^8.0.26",
46 | "@types/rimraf": "^2.0.2",
47 | "@types/ws": "^3.0.2",
48 | "commonmark": "^0.27.0",
49 | "cross-env": "^5.0.5",
50 | "eslint": "^4.0.0",
51 | "esprima": "^4.0.0",
52 | "markdown-toc": "^1.1.0",
53 | "minimist": "^1.2.0",
54 | "ncp": "^2.0.0",
55 | "pdfjs-dist": "^1.8.595",
56 | "pixelmatch": "^4.0.2",
57 | "pngjs": "^3.2.0",
58 | "text-diff": "^1.0.1",
59 | "typescript": "^2.6.0-rc"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/Dialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {helper} = require('./helper');
18 |
19 | class Dialog {
20 | /**
21 | * @param {!Puppeteer.CDPSession} client
22 | * @param {string} type
23 | * @param {string} message
24 | * @param {(string|undefined)} defaultValue
25 | */
26 | constructor(client, type, message, defaultValue = '') {
27 | this._client = client;
28 | this._type = type;
29 | this._message = message;
30 | this._handled = false;
31 | this._defaultValue = defaultValue;
32 | }
33 |
34 | /**
35 | * @return {string}
36 | */
37 | type() {
38 | return this._type;
39 | }
40 |
41 | /**
42 | * @return {string}
43 | */
44 | message() {
45 | return this._message;
46 | }
47 |
48 | /**
49 | * @return {string}
50 | */
51 | defaultValue() {
52 | return this._defaultValue;
53 | }
54 |
55 | /**
56 | * @param {string=} promptText
57 | */
58 | async accept(promptText) {
59 | console.assert(!this._handled, 'Cannot accept dialog which is already handled!');
60 | this._handled = true;
61 | await this._client.send('Page.handleJavaScriptDialog', {
62 | accept: true,
63 | promptText: promptText
64 | });
65 | }
66 |
67 | async dismiss() {
68 | console.assert(!this._handled, 'Cannot dismiss dialog which is already handled!');
69 | this._handled = true;
70 | await this._client.send('Page.handleJavaScriptDialog', {
71 | accept: false
72 | });
73 | }
74 | }
75 |
76 | Dialog.Type = {
77 | Alert: 'alert',
78 | BeforeUnload: 'beforeunload',
79 | Confirm: 'confirm',
80 | Prompt: 'prompt'
81 | };
82 |
83 | module.exports = Dialog;
84 | helper.tracePublicAPI(Dialog);
85 |
--------------------------------------------------------------------------------
/lib/Multimap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class Multimap {
18 | constructor() {
19 | this._map = new Map();
20 | }
21 |
22 | set(key, value) {
23 | let set = this._map.get(key);
24 | if (!set) {
25 | set = new Set();
26 | this._map.set(key, set);
27 | }
28 | set.add(value);
29 | }
30 |
31 | get(key) {
32 | let result = this._map.get(key);
33 | if (!result)
34 | result = new Set();
35 | return result;
36 | }
37 |
38 | has(key) {
39 | return this._map.has(key);
40 | }
41 |
42 | hasValue(key, value) {
43 | const set = this._map.get(key);
44 | if (!set)
45 | return false;
46 | return set.has(value);
47 | }
48 |
49 | /**
50 | * @return {number}
51 | */
52 | get size() {
53 | return this._map.size;
54 | }
55 |
56 | delete(key, value) {
57 | const values = this.get(key);
58 | const result = values.delete(value);
59 | if (!values.size)
60 | this._map.delete(key);
61 | return result;
62 | }
63 |
64 | deleteAll(key) {
65 | this._map.delete(key);
66 | }
67 |
68 | firstValue(key) {
69 | const set = this._map.get(key);
70 | if (!set)
71 | return null;
72 | return set.values().next().value;
73 | }
74 |
75 | firstKey() {
76 | return this._map.keys().next().value;
77 | }
78 |
79 | valuesArray() {
80 | const result = [];
81 | for (const key of this._map.keys())
82 | result.push(...Array.from(this._map.get(key).values()));
83 | return result;
84 | }
85 |
86 | keysArray() {
87 | return Array.from(this._map.keys());
88 | }
89 |
90 | clear() {
91 | this._map.clear();
92 | }
93 | }
94 |
95 | module.exports = Multimap;
96 |
--------------------------------------------------------------------------------
/utils/testrunner/Multimap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class Multimap {
18 | constructor() {
19 | this._map = new Map();
20 | }
21 |
22 | set(key, value) {
23 | let set = this._map.get(key);
24 | if (!set) {
25 | set = new Set();
26 | this._map.set(key, set);
27 | }
28 | set.add(value);
29 | }
30 |
31 | get(key) {
32 | let result = this._map.get(key);
33 | if (!result)
34 | result = new Set();
35 | return result;
36 | }
37 |
38 | has(key) {
39 | return this._map.has(key);
40 | }
41 |
42 | hasValue(key, value) {
43 | const set = this._map.get(key);
44 | if (!set)
45 | return false;
46 | return set.has(value);
47 | }
48 |
49 | /**
50 | * @return {number}
51 | */
52 | get size() {
53 | return this._map.size;
54 | }
55 |
56 | delete(key, value) {
57 | const values = this.get(key);
58 | const result = values.delete(value);
59 | if (!values.size)
60 | this._map.delete(key);
61 | return result;
62 | }
63 |
64 | deleteAll(key) {
65 | this._map.delete(key);
66 | }
67 |
68 | firstValue(key) {
69 | const set = this._map.get(key);
70 | if (!set)
71 | return null;
72 | return set.values().next().value;
73 | }
74 |
75 | firstKey() {
76 | return this._map.keys().next().value;
77 | }
78 |
79 | valuesArray() {
80 | const result = [];
81 | for (const key of this._map.keys())
82 | result.push(...Array.from(this._map.get(key).values()));
83 | return result;
84 | }
85 |
86 | keysArray() {
87 | return Array.from(this._map.keys());
88 | }
89 |
90 | clear() {
91 | this._map.clear();
92 | }
93 | }
94 |
95 | module.exports = Multimap;
96 |
--------------------------------------------------------------------------------
/test/assets/input/select.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Selection Test
5 |
6 |
7 |
24 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/test/frame-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const utils = module.exports = {
18 | /**
19 | * @param {!Page} page
20 | * @param {string} frameId
21 | * @param {string} url
22 | */
23 | attachFrame: async function(page, frameId, url) {
24 | await page.evaluate(attachFrame, frameId, url);
25 |
26 | function attachFrame(frameId, url) {
27 | const frame = document.createElement('iframe');
28 | frame.src = url;
29 | frame.id = frameId;
30 | document.body.appendChild(frame);
31 | return new Promise(x => frame.onload = x);
32 | }
33 | },
34 |
35 | /**
36 | * @param {!Page} page
37 | * @param {string} frameId
38 | */
39 | detachFrame: async function(page, frameId) {
40 | await page.evaluate(detachFrame, frameId);
41 |
42 | function detachFrame(frameId) {
43 | const frame = document.getElementById(frameId);
44 | frame.remove();
45 | }
46 | },
47 |
48 | /**
49 | * @param {!Page} page
50 | * @param {string} frameId
51 | * @param {string} url
52 | */
53 | navigateFrame: async function(page, frameId, url) {
54 | await page.evaluate(navigateFrame, frameId, url);
55 |
56 | function navigateFrame(frameId, url) {
57 | const frame = document.getElementById(frameId);
58 | frame.src = url;
59 | return new Promise(x => frame.onload = x);
60 | }
61 | },
62 |
63 | /**
64 | * @param {!Frame} frame
65 | * @param {string=} indentation
66 | * @return {string}
67 | */
68 | dumpFrames: function(frame, indentation) {
69 | indentation = indentation || '';
70 | let result = indentation + frame.url().replace(/:\d{4}\//, ':/');
71 | for (const child of frame.childFrames())
72 | result += '\n' + utils.dumpFrames(child, ' ' + indentation);
73 | return result;
74 | },
75 | };
76 |
--------------------------------------------------------------------------------
/test/assets/detect-touch.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Detect Touch Test
5 |
10 |
11 |
12 |
15 |
16 |
--------------------------------------------------------------------------------
/utils/doclint/preprocessor/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const preprocessor = require('.');
18 | const SourceFactory = require('../SourceFactory');
19 | const factory = new SourceFactory();
20 | const VERSION = require('../../../package.json').version;
21 |
22 | const {TestRunner, Reporter, Matchers} = require('../../testrunner/');
23 | const runner = new TestRunner();
24 | new Reporter(runner);
25 |
26 | const {describe, xdescribe, fdescribe} = runner;
27 | const {it, fit, xit} = runner;
28 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
29 | const {expect} = new Matchers();
30 |
31 | describe('preprocessor', function() {
32 | it('should throw for unknown command', function() {
33 | const source = factory.createForTest('doc.md', getCommand('unknownCommand()'));
34 | const messages = preprocessor([source]);
35 | expect(source.hasUpdatedText()).toBe(false);
36 | expect(messages.length).toBe(1);
37 | expect(messages[0].type).toBe('error');
38 | expect(messages[0].text).toContain('Unknown command');
39 | });
40 | describe('gen:version', function() {
41 | it('should work', function() {
42 | const source = factory.createForTest('doc.md', `Puppeteer v${getCommand('version')}`);
43 | const messages = preprocessor([source]);
44 | expect(messages.length).toBe(1);
45 | expect(messages[0].type).toBe('warning');
46 | expect(messages[0].text).toContain('doc.md');
47 | expect(source.text()).toBe(`Puppeteer v${getCommand('version', VERSION)}`);
48 | });
49 | it('should tolerate different writing', function() {
50 | const source = factory.createForTest('doc.md', `Puppeteer vWHAT
51 | `);
52 | preprocessor([source]);
53 | expect(source.text()).toBe(`Puppeteer v${VERSION}`);
54 | });
55 | it('should not tolerate missing gen:stop', function() {
56 | const source = factory.createForTest('doc.md', ``);
57 | const messages = preprocessor([source]);
58 | expect(source.hasUpdatedText()).toBe(false);
59 | expect(messages.length).toBe(1);
60 | expect(messages[0].type).toBe('error');
61 | expect(messages[0].text).toContain(`Failed to find 'gen:stop'`);
62 | });
63 | });
64 | });
65 |
66 | runner.run();
67 |
68 | function getCommand(name, body = '') {
69 | return `${body}`;
70 | }
71 |
--------------------------------------------------------------------------------
/lib/Tracing.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | const {helper} = require('./helper');
17 | const fs = require('fs');
18 |
19 | const openAsync = helper.promisify(fs.open);
20 | const writeAsync = helper.promisify(fs.write);
21 | const closeAsync = helper.promisify(fs.close);
22 |
23 | class Tracing {
24 | /**
25 | * @param {!Puppeteer.CDPSession} client
26 | */
27 | constructor(client) {
28 | this._client = client;
29 | this._recording = false;
30 | this._path = '';
31 | }
32 |
33 | /**
34 | * @param {!Object} options
35 | */
36 | async start(options) {
37 | console.assert(!this._recording, 'Cannot start recording trace while already recording trace.');
38 | console.assert(options.path, 'Must specify a path to write trace file to.');
39 |
40 | const defaultCategories = [
41 | '-*', 'devtools.timeline', 'v8.execute', 'disabled-by-default-devtools.timeline',
42 | 'disabled-by-default-devtools.timeline.frame', 'toplevel',
43 | 'blink.console', 'blink.user_timing', 'latencyInfo', 'disabled-by-default-devtools.timeline.stack',
44 | 'disabled-by-default-v8.cpu_profiler'
45 | ];
46 | const categoriesArray = options.categories || defaultCategories;
47 |
48 | if (options.screenshots)
49 | categoriesArray.push('disabled-by-default-devtools.screenshot');
50 |
51 | this._path = options.path;
52 | this._recording = true;
53 | await this._client.send('Tracing.start', {
54 | transferMode: 'ReturnAsStream',
55 | categories: categoriesArray.join(',')
56 | });
57 | }
58 |
59 | async stop() {
60 | let fulfill;
61 | const contentPromise = new Promise(x => fulfill = x);
62 | this._client.once('Tracing.tracingComplete', event => {
63 | this._readStream(event.stream, this._path).then(fulfill);
64 | });
65 | await this._client.send('Tracing.end');
66 | this._recording = false;
67 | return contentPromise;
68 | }
69 |
70 | /**
71 | * @param {string} handle
72 | * @param {string} path
73 | */
74 | async _readStream(handle, path) {
75 | let eof = false;
76 | const file = await openAsync(path, 'w');
77 | while (!eof) {
78 | const response = await this._client.send('IO.read', {handle});
79 | eof = response.eof;
80 | if (path)
81 | await writeAsync(file, response.data);
82 | }
83 | await closeAsync(file);
84 | await this._client.send('IO.close', {handle});
85 | }
86 | }
87 | helper.tracePublicAPI(Tracing);
88 |
89 | module.exports = Tracing;
90 |
--------------------------------------------------------------------------------
/utils/doclint/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /**
3 | * Copyright 2017 Google Inc. All rights reserved.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | const puppeteer = require('../..');
19 | const path = require('path');
20 | const SourceFactory = require('./SourceFactory');
21 |
22 | const PROJECT_DIR = path.join(__dirname, '..', '..');
23 |
24 | const RED_COLOR = '\x1b[31m';
25 | const YELLOW_COLOR = '\x1b[33m';
26 | const RESET_COLOR = '\x1b[0m';
27 |
28 | run();
29 |
30 | async function run() {
31 | const startTime = Date.now();
32 |
33 | const sourceFactory = new SourceFactory();
34 | /** @type {!Array} */
35 | const messages = [];
36 |
37 | // Documentation checks.
38 | {
39 | const apiSource = await sourceFactory.readFile(path.join(PROJECT_DIR, 'docs', 'api.md'));
40 | const mdSources = [apiSource];
41 |
42 | const toc = require('./toc');
43 | messages.push(...await toc(mdSources));
44 |
45 | const preprocessor = require('./preprocessor');
46 | messages.push(...await preprocessor(mdSources));
47 |
48 | const browser = await puppeteer.launch({args: ['--no-sandbox']});
49 | const page = await browser.newPage();
50 | const checkPublicAPI = require('./check_public_api');
51 | const jsSources = await sourceFactory.readdir(path.join(PROJECT_DIR, 'lib'), '.js');
52 | messages.push(...await checkPublicAPI(page, mdSources, jsSources));
53 | await browser.close();
54 | }
55 |
56 | // Report results.
57 | const errors = messages.filter(message => message.type === 'error');
58 | if (errors.length) {
59 | console.log('DocLint Failures:');
60 | for (let i = 0; i < errors.length; ++i) {
61 | let error = errors[i].text;
62 | error = error.split('\n').join('\n ');
63 | console.log(` ${i + 1}) ${RED_COLOR}${error}${RESET_COLOR}`);
64 | }
65 | }
66 | const warnings = messages.filter(message => message.type === 'warning');
67 | if (warnings.length) {
68 | console.log('DocLint Warnings:');
69 | for (let i = 0; i < warnings.length; ++i) {
70 | let warning = warnings[i].text;
71 | warning = warning.split('\n').join('\n ');
72 | console.log(` ${i + 1}) ${YELLOW_COLOR}${warning}${RESET_COLOR}`);
73 | }
74 | }
75 | let clearExit = messages.length === 0;
76 | if (await sourceFactory.saveChangedSources()) {
77 | if (clearExit)
78 | console.log(`${YELLOW_COLOR}Some files were updated.${RESET_COLOR}`);
79 | clearExit = false;
80 | }
81 | console.log(`${errors.length} failures, ${warnings.length} warnings.`);
82 | const runningTime = Date.now() - startTime;
83 | console.log(`DocLint Finished in ${runningTime / 1000} seconds`);
84 | process.exit(clearExit ? 0 : 1);
85 | }
86 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/Documentation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class Documentation {
18 | /**
19 | * @param {!Array} clasesArray
20 | */
21 | constructor(classesArray) {
22 | this.classesArray = classesArray;
23 | this.classes = new Map();
24 | for (const cls of classesArray)
25 | this.classes.set(cls.name, cls);
26 | }
27 | }
28 |
29 | Documentation.Class = class {
30 | /**
31 | * @param {string} name
32 | * @param {!Array} membersArray
33 | */
34 | constructor(name, membersArray) {
35 | this.name = name;
36 | this.membersArray = membersArray;
37 | this.members = new Map();
38 | this.properties = new Map();
39 | this.methods = new Map();
40 | this.events = new Map();
41 | for (const member of membersArray) {
42 | this.members.set(member.name, member);
43 | if (member.type === 'method')
44 | this.methods.set(member.name, member);
45 | else if (member.type === 'property')
46 | this.properties.set(member.name, member);
47 | else if (member.type === 'event')
48 | this.events.set(member.name, member);
49 | }
50 | }
51 | };
52 |
53 | Documentation.Member = class {
54 | /**
55 | * @param {string} type
56 | * @param {string} name
57 | * @param {!Array} argsArray
58 | * @param {boolean} hasReturn
59 | * @param {boolean} async
60 | */
61 | constructor(type, name, argsArray, hasReturn, async) {
62 | this.type = type;
63 | this.name = name;
64 | this.argsArray = argsArray;
65 | this.args = new Map();
66 | this.hasReturn = hasReturn;
67 | this.async = async;
68 | for (const arg of argsArray)
69 | this.args.set(arg.name, arg);
70 | }
71 |
72 | /**
73 | * @param {string} name
74 | * @param {!Array} argsArray
75 | * @param {boolean} hasReturn
76 | * @return {!Documentation.Member}
77 | */
78 | static createMethod(name, argsArray, hasReturn, async) {
79 | return new Documentation.Member('method', name, argsArray, hasReturn, async);
80 | }
81 |
82 | /**
83 | * @param {string} name
84 | * @return {!Documentation.Member}
85 | */
86 | static createProperty(name) {
87 | return new Documentation.Member('property', name, [], false, false);
88 | }
89 |
90 | /**
91 | * @param {string} name
92 | * @return {!Documentation.Member}
93 | */
94 | static createEvent(name) {
95 | return new Documentation.Member('event', name, [], false, false);
96 | }
97 | };
98 |
99 | Documentation.Argument = class {
100 | /**
101 | * @param {string} name
102 | */
103 | constructor(name) {
104 | this.name = name;
105 | }
106 | };
107 |
108 | module.exports = Documentation;
109 |
110 |
--------------------------------------------------------------------------------
/utils/doclint/preprocessor/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const Message = require('../Message');
18 |
19 | const PUPPETEER_VERSION = require('../../../package.json').version;
20 |
21 | module.exports = function(sources) {
22 | const messages = [];
23 | let commands = [];
24 | for (const source of sources) {
25 | const text = source.text();
26 | const commandStartRegex = //ig;
27 | const commandEndRegex = //ig;
28 | let start;
29 |
30 | while (start = commandStartRegex.exec(text)) { // eslint-disable-line no-cond-assign
31 | commandEndRegex.lastIndex = commandStartRegex.lastIndex;
32 | const end = commandEndRegex.exec(text);
33 | if (!end) {
34 | messages.push(Message.error(`Failed to find 'gen:stop' for comamnd ${start[0]}`));
35 | break;
36 | }
37 | const name = start[1];
38 | const arg = start[2];
39 | const from = commandStartRegex.lastIndex;
40 | const to = end.index;
41 | commandStartRegex.lastIndex = commandEndRegex.lastIndex;
42 | commands.push({name, arg, from, to, source});
43 | }
44 | }
45 |
46 | commands = validateCommands(commands, messages);
47 |
48 | const changedSources = new Set();
49 | // Iterate commands in reverse order so that edits don't conflict.
50 | commands.sort((a, b) => b.from - a.from);
51 | for (const command of commands) {
52 | let newText = command.source.text();
53 | if (command.name === 'version')
54 | newText = replaceInText(newText, command.from, command.to, PUPPETEER_VERSION);
55 | if (command.source.setText(newText))
56 | changedSources.add(command.source);
57 | }
58 | for (const source of changedSources)
59 | messages.push(Message.warning(`GEN: updated ${source.projectPath()}`));
60 | return messages;
61 | };
62 |
63 | /**
64 | * @param {!Array} commands
65 | * @param {!Array} outMessages
66 | * @return {!Array}
67 | */
68 | function validateCommands(commands, outMessages) {
69 | // Filter sane commands
70 | const goodCommands = commands.filter(command => {
71 | if (command.name === 'version')
72 | return check(command, !command.arg, `"gen:version" should not have argument`);
73 | check(command, false, `Unknown command: "gen:${command.name}"`);
74 | });
75 |
76 | return goodCommands;
77 |
78 | function check(command, condition, message) {
79 | if (condition)
80 | return true;
81 | outMessages.push(Message.error(`${command.source.projectPath()}: ${message}`));
82 | return false;
83 | }
84 | }
85 |
86 | /**
87 | * @param {string} text
88 | * @param {number} from
89 | * @param {number} to
90 | * @param {string} newText
91 | * @return {string}
92 | */
93 | function replaceInText(text, from, to, newText) {
94 | return text.substring(0, from) + newText + text.substring(to);
95 | }
96 |
--------------------------------------------------------------------------------
/lib/EmulationManager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class EmulationManager {
18 | /**
19 | * @param {!Puppeteer.CDPSession} client
20 | */
21 | constructor(client) {
22 | this._client = client;
23 | this._emulatingMobile = false;
24 | this._injectedTouchScriptId = null;
25 | }
26 |
27 | /**
28 | * @param {!EmulationManager.Viewport} viewport
29 | * @return {Promise}
30 | */
31 | async emulateViewport(client, viewport) {
32 | const mobile = viewport.isMobile || false;
33 | const width = viewport.width;
34 | const height = viewport.height;
35 | const deviceScaleFactor = viewport.deviceScaleFactor || 1;
36 | const screenOrientation = viewport.isLandscape ? { angle: 90, type: 'landscapePrimary' } : { angle: 0, type: 'portraitPrimary' };
37 |
38 | await Promise.all([
39 | this._client.send('Emulation.setDeviceMetricsOverride', { mobile, width, height, deviceScaleFactor, screenOrientation }),
40 | this._client.send('Emulation.setTouchEmulationEnabled', {
41 | enabled: viewport.hasTouch || false,
42 | configuration: viewport.isMobile ? 'mobile' : 'desktop'
43 | })
44 | ]);
45 |
46 | let reloadNeeded = false;
47 | if (viewport.hasTouch && !this._injectedTouchScriptId) {
48 | const source = `(${injectedTouchEventsFunction})()`;
49 | this._injectedTouchScriptId = (await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source })).identifier;
50 | reloadNeeded = true;
51 | } else if (!viewport.hasTouch && this._injectedTouchScriptId) {
52 | await this._client.send('Page.removeScriptToEvaluateOnNewDocument', {identifier: this._injectedTouchScriptId});
53 | this._injectedTouchScriptId = null;
54 | reloadNeeded = true;
55 | }
56 |
57 | if (this._emulatingMobile !== mobile)
58 | reloadNeeded = true;
59 | this._emulatingMobile = mobile;
60 | return reloadNeeded;
61 |
62 | function injectedTouchEventsFunction() {
63 | const touchEvents = ['ontouchstart', 'ontouchend', 'ontouchmove', 'ontouchcancel'];
64 | // @ts-ignore
65 | const recepients = [window.__proto__, document.__proto__];
66 | for (let i = 0; i < touchEvents.length; ++i) {
67 | for (let j = 0; j < recepients.length; ++j) {
68 | if (!(touchEvents[i] in recepients[j])) {
69 | Object.defineProperty(recepients[j], touchEvents[i], {
70 | value: null, writable: true, configurable: true, enumerable: true
71 | });
72 | }
73 | }
74 | }
75 | }
76 | }
77 | }
78 |
79 | /**
80 | * @typedef {Object} EmulationManager.Viewport
81 | * @property {number} width
82 | * @property {number} height
83 | * @property {number=} deviceScaleFactor
84 | * @property {boolean=} isMobile
85 | * @property {boolean=} isLandscape
86 | * @property {boolean=} hasTouch
87 | */
88 |
89 | module.exports = EmulationManager;
90 |
--------------------------------------------------------------------------------
/test/server/key.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN ENCRYPTED PRIVATE KEY-----
2 | MIIJjjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIT2qHBljWwsICAggA
3 | MBQGCCqGSIb3DQMHBAh5lMoGsb1UgQSCCUg/FtxOBSUG05DteTzzkdp6FptfOy5p
4 | iirYKWUNck6EhPdZ1RPSuCvs84S+qP6Po5gFfGz9e7tdbNSueOQnMD+vbS8vXpZU
5 | 4KVafxUlKbAfSVj2ZY6SkbU8v/iXiTieUj3G046z5QYsfcvyEdS47Z+WbKAJKemv
6 | BVck4W+/gKtiukbK/94VaehC7kbbO2yp9/kYp9qqk33aB61C8xX/fILOIRdYkvut
7 | 55pHGviN5Eajabm/d83MPAzaqUdv9NiIKej2IFXs+Oz8OYRbVVHwyN93HquHmn+V
8 | WKOknJXzvS5Pa+ViGmB4o6+fpluuIyHXJUUG20z/IY5n0vaBo4jRlyYZ3J1vWTZ2
9 | 1bhLGPUPUlGt1vToY5Ejr8qxaYGxLEOOxvRL3Z1xnlCbv2tQTD3TyV+Yr7i/54s4
10 | 2h0ddnP22KDmKEA8PhxZMZG7i7fsO+yA6hbKeMnS8lGv5Yyw2H7vRm4JwgeuX7A7
11 | gPQ/zQ6Sf6fUCkOMgVD3jJZ4XK7l9GkEQMYJAoowYb+WJ+yHSUQWJLJzPnNqhAPT
12 | aqQGHAO+AjGDnSI6ANDlxUubfU4yjQPg2eVyFczS7G+BISgCrJyQP/Z3Mpzo5rdu
13 | iTVcH8Phvsdm3PBOTkbgqxeHos6tyIiH1aElwlG3hxM1QiM6D9p7b5RyjUqfP/qn
14 | r1CndzroAyvseQ3ET8mJQAqWdGd+qmw9t0Voi7sR9jcDLVw+oLSk+oD8dk6BrItI
15 | rb39DmQAJiP7sLtAZ/zUm3sRpGgytNjElzzDdmijCM1A5oJzLvRwwa4AHwicMBkW
16 | nRnvliockHhFHQnQ/QThNGTPoNOHHgWwUKIidSFAdPIK1tYJP/4te2t5Djm25jS7
17 | ITm/LR1gbUguOMlOqwrDyAQir5Jjjk2pqN30+W9AXgVk/Oq/bWC8eMRv8zsmBlUY
18 | poXBwV52ITCzpNOsywSpz1vWGs1I0WQcLbm1zrHCR3CYxPOB45XdvHtwnHn5zaLF
19 | NB42IS/zK4PYzSJrbwJMsILS51Qb88JNL4PvYMCz43dUd0W6hokX4yCnQjfeEvB1
20 | MIhYQBu/lFK3IsskgoYBeg4zbU950vKSL+oNb5/dzAoayyJ/jRg5a1xMx8tCHazW
21 | 5afYxyIE9FRThwsHQ7K8BGe/4qwMCbvDIh/Fk+tWqXiyJJdsixf6YZLXtQXPkzki
22 | azpN9plUSoWpTVY4i6wNF5IO5LExLWlemDnw1v99lnU5W3SfgEwV8DYAyScA7Qzd
23 | GJQEfZVPSSxMQSyfrAap80PVs5KZWYcJpzKl9vwwdzopA2oj2vvx40r43YFiVFHp
24 | IpG9biWgh6McKKVCT1QThtNktDS9NT4uQZofC6m+RzzbsfI2R9IelozSOrS3xRJd
25 | D1eTiIccuomOsrhnh+/VDc9iuP6LgGaKwjdNSfcryuOn3S2+vho7wGrul/GxRf5k
26 | GrT1C68y5e0C/qbxyKtJPEGcntgLADhVrPr3WiM2M4tZ/imMi9XeyKSZ3i6P0OB/
27 | QkhfNLrws03nXi3ASpk7/C9EWnnAYGwxQRht2LpEcTOpCUmmZ+6cBM4+dXMjvxqU
28 | SXR93Vm7Rwfs3MSIbtD2lGEXaIvG5mOh/9HXByBOh/UuSATeUIgEWgfW1zn0tvAw
29 | muqOyeS+ngYtoRK0NV979Kp7Pizq7ZHpoemm+C65EVvq2CU/ceRNh6DwCW5ZCHvU
30 | rJdOXqdO9Oef/2rHmLjnkwIjkXS3MJFd9wlCsSJn2SsuNmSLkwXuEkgdbjMKwjW+
31 | sKzozd6FDp80HBuNw6H+kBn8KhO9tdQmyZnS9EpwO+OLgCTuyDNBiA892aevx9zD
32 | 7WPzNlsppcMEcxudv0mvavMfUJhbQ0wo+9Rp3wRQXIquKt++5vK7EoPvUjhVO39p
33 | 1VJTfx/wnX39VF62hc+OH3rritHmsrCBwzPupDGWSLBwQcMJbmgjFojuMG02pkIM
34 | mR5HIp+Xl4fWmSa9aklVyssdSi1hK+6VRIafTPtCiyA2BfeeYQk1xEsjSai0TYqh
35 | Suw+3wKOORm0xPEV30qX08N3bCP7aqqA+dMXWjfDM7LUBzDV4+MX6WOMdunlBdIS
36 | 6q63DXon6CCLoXPun/IwUEhOZfh43UAF3qEfxJdkhH6AgPdCSYBB5AWHQDdqvrBY
37 | wkhPEXkY2viMF2hOknXQr1GAdx0EjnAaMMxDE7+q01ERdgnO0xRVOD/4DjngdU4K
38 | p935fERs5/1nWe4yq/z8lXEC2W3YdiPR3Ok+pekyzhokh5/b+eG8G0JEnnl4+M8y
39 | qqyZZTJJeDFhasOzxwc5hwq8yqOznF6dEF58FXO9pKqBXbz5NrwbdUPtp67WwNx7
40 | iTRNe9szns5U8/3S5LEg+xGvAUhwbdmWxzYHye2MAt4sObi4YFDb1RVkZzCj2eer
41 | 6hzCQi5VUB3pUqr8in1TJMXPzCrl4ZdK2KoHtNA1gKT7midLzsjP+rXsgKtzcGAO
42 | IcNOkbv8KqzYr2OtcEW+v9z6vgSpKImQ/n+6/WLRGWh9pLCZ+onD06OLcsm55H4y
43 | zNacau+nT+Mf9XQTErFjj2+orjF3p2u3tY9vpoLJN653mMTNzexVZi+k9NgZz7eL
44 | 4m8R3ai0ZeGQezo9rGHQ6ulqlueN0qXGKI07G4VKxM35OZeOyPPnrF95FQzMY5Ox
45 | GkQr1hR0rj+oQJTFWIfy9ffZEBzUqC8fYYvOPJO32I+fe2tvBUtpUe3w7dYHVs3a
46 | lqi/+Wwk0QLE4ItR5f0sZsoIeHkDeXoKYrCOOPl2bnIQA3UT/rszGl6YDY8pU/7W
47 | fziO/G5BzGe7LU7o26ykzkmDF5alUaNdfVnnQcm/iRy8YOsjfKQmqbFfFCKOlRMo
48 | RdTStOcsEzddttI57YlBHv5Gh1GfMjd5acgeQfStSMwHcahwY2Y36WcR4GE4Id/5
49 | ycaikCt+zYEVKUoO+JEZxvzFQIPVhT3oBOjSFj/EeMLmnGMjnohdgEmh2T7+0Ko5
50 | oAMzWW/XNeJlL1DAPPEpya66oSvwZvxUkGDLcIGYWgzSKz0JO8zn6wDTOU7O2YeF
51 | Gm8YymS0oqnK1wE6HJ1YSk2ers6kgZij3kVAnGlbI88YLeMJvteO2oL7GqfEQdSA
52 | HskQklLZUT2Y+m7oinVyCcdbKRGI9frnfm6j3TVkrNHjcc7aKhM+XT5ZNKxT3ftt
53 | 4Ig=
54 | -----END ENCRYPTED PRIVATE KEY-----
55 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "root": true,
3 |
4 | "env": {
5 | "node": true,
6 | "es6": true
7 | },
8 |
9 | "parserOptions": {
10 | "ecmaVersion": 8
11 | },
12 |
13 | /**
14 | * ESLint rules
15 | *
16 | * All available rules: http://eslint.org/docs/rules/
17 | *
18 | * Rules take the following form:
19 | * "rule-name", [severity, { opts }]
20 | * Severity: 2 == error, 1 == warning, 0 == off.
21 | */
22 | "rules": {
23 | /**
24 | * Enforced rules
25 | */
26 |
27 |
28 | // syntax preferences
29 | "quotes": [2, "single", {
30 | "avoidEscape": true,
31 | "allowTemplateLiterals": true
32 | }],
33 | "semi": 2,
34 | "no-extra-semi": 2,
35 | "comma-style": [2, "last"],
36 | "wrap-iife": [2, "inside"],
37 | "spaced-comment": [2, "always", {
38 | "markers": ["*"]
39 | }],
40 | "eqeqeq": [2],
41 | "arrow-body-style": [2, "as-needed"],
42 | "accessor-pairs": [2, {
43 | "getWithoutSet": false,
44 | "setWithoutGet": false
45 | }],
46 | "brace-style": [2, "1tbs", {"allowSingleLine": true}],
47 | "curly": [2, "multi-or-nest", "consistent"],
48 | "new-parens": 2,
49 | "func-call-spacing": 2,
50 | "arrow-parens": [2, "as-needed"],
51 | "prefer-const": 2,
52 | "quote-props": [2, "consistent"],
53 |
54 | // anti-patterns
55 | "no-var": 2,
56 | "no-with": 2,
57 | "no-multi-str": 2,
58 | "no-caller": 2,
59 | "no-implied-eval": 2,
60 | "no-labels": 2,
61 | "no-new-object": 2,
62 | "no-octal-escape": 2,
63 | "no-self-compare": 2,
64 | "no-shadow-restricted-names": 2,
65 | "no-cond-assign": 2,
66 | "no-debugger": 2,
67 | "no-dupe-keys": 2,
68 | "no-duplicate-case": 2,
69 | "no-empty-character-class": 2,
70 | "no-unreachable": 2,
71 | "no-unsafe-negation": 2,
72 | "radix": 2,
73 | "valid-typeof": 2,
74 | "no-unused-vars": [2, { "args": "none", "vars": "local", "varsIgnorePattern": "([fx]?describe|[fx]?it|beforeAll|beforeEach|afterAll|afterEach)" }],
75 | "no-implicit-globals": [2],
76 |
77 | // es2015 features
78 | "require-yield": 2,
79 | "template-curly-spacing": [2, "never"],
80 |
81 | // spacing details
82 | "space-infix-ops": 2,
83 | "space-in-parens": [2, "never"],
84 | "space-before-function-paren": [2, "never"],
85 | "no-whitespace-before-property": 2,
86 | "keyword-spacing": [2, {
87 | "overrides": {
88 | "if": {"after": true},
89 | "else": {"after": true},
90 | "for": {"after": true},
91 | "while": {"after": true},
92 | "do": {"after": true},
93 | "switch": {"after": true},
94 | "return": {"after": true}
95 | }
96 | }],
97 | "arrow-spacing": [2, {
98 | "after": true,
99 | "before": true
100 | }],
101 |
102 | // file whitespace
103 | "no-multiple-empty-lines": [2, {"max": 2}],
104 | "no-mixed-spaces-and-tabs": 2,
105 | "no-trailing-spaces": 2,
106 | "linebreak-style": [ process.platform === "win32" ? 0 : 2, "unix" ],
107 | "indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
108 | "key-spacing": [2, {
109 | "beforeColon": false
110 | }]
111 | }
112 | };
113 |
--------------------------------------------------------------------------------
/utils/testrunner/Matchers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | module.exports = class Matchers {
18 | constructor(customMatchers = {}) {
19 | this._matchers = {};
20 | Object.assign(this._matchers, DefaultMatchers);
21 | Object.assign(this._matchers, customMatchers);
22 | this.expect = this.expect.bind(this);
23 | }
24 |
25 | addMatcher(name, matcher) {
26 | this._matchers[name] = matcher;
27 | }
28 |
29 | expect(value) {
30 | return new Expect(value, this._matchers);
31 | }
32 | };
33 |
34 | class Expect {
35 | constructor(value, matchers) {
36 | this.not = {};
37 | this.not.not = this;
38 | for (const matcherName of Object.keys(matchers)) {
39 | const matcher = matchers[matcherName];
40 | this[matcherName] = applyMatcher.bind(null, matcherName, matcher, false, value);
41 | this.not[matcherName] = applyMatcher.bind(null, matcherName, matcher, true, value);
42 | }
43 |
44 | function applyMatcher(matcherName, matcher, inverse, value, ...args) {
45 | const result = matcher.call(null, value, ...args);
46 | const message = `expect.${matcherName} failed` + (result.message ? `: ${result.message}` : '');
47 | console.assert(result.pass !== inverse, message);
48 | }
49 | }
50 | }
51 |
52 | const DefaultMatchers = {
53 | toBe: function(value, other, message) {
54 | message = message || `${value} == ${other}`;
55 | return { pass: value === other, message };
56 | },
57 |
58 | toBeFalsy: function(value, message) {
59 | message = message || `${value}`;
60 | return { pass: !value, message };
61 | },
62 |
63 | toBeTruthy: function(value, message) {
64 | message = message || `${value}`;
65 | return { pass: !!value, message };
66 | },
67 |
68 | toBeGreaterThan: function(value, other, message) {
69 | message = message || `${value} > ${other}`;
70 | return { pass: value > other, message };
71 | },
72 |
73 | toBeGreaterThanOrEqual: function(value, other, message) {
74 | message = message || `${value} >= ${other}`;
75 | return { pass: value >= other, message };
76 | },
77 |
78 | toBeLessThan: function(value, other, message) {
79 | message = message || `${value} < ${other}`;
80 | return { pass: value < other, message };
81 | },
82 |
83 | toBeLessThanOrEqual: function(value, other, message) {
84 | message = message || `${value} <= ${other}`;
85 | return { pass: value <= other, message };
86 | },
87 |
88 | toBeNull: function(value, message) {
89 | message = message || `${value} == null`;
90 | return { pass: value === null, message };
91 | },
92 |
93 | toContain: function(value, other, message) {
94 | message = message || `${value} ⊇ ${other}`;
95 | return { pass: value.includes(other), message };
96 | },
97 |
98 | toEqual: function(value, other, message) {
99 | message = message || `${JSON.stringify(value)} ≈ ${JSON.stringify(other)}`;
100 | return { pass: JSON.stringify(value) === JSON.stringify(other), message };
101 | },
102 |
103 | toBeCloseTo: function(value, other, precision, message) {
104 | return {
105 | pass: Math.abs(value - other) < Math.pow(10, -precision),
106 | message
107 | };
108 | }
109 | };
110 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/test/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const path = require('path');
18 | const puppeteer = require('../../../..');
19 | const checkPublicAPI = require('..');
20 | const SourceFactory = require('../../SourceFactory');
21 | const mdBuilder = require('../MDBuilder');
22 | const jsBuilder = require('../JSBuilder');
23 | const GoldenUtils = require('../../../../test/golden-utils');
24 |
25 | const {TestRunner, Reporter, Matchers} = require('../../../testrunner/');
26 | const runner = new TestRunner();
27 | const reporter = new Reporter(runner);
28 |
29 | const {describe, xdescribe, fdescribe} = runner;
30 | const {it, fit, xit} = runner;
31 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
32 |
33 | let browser;
34 | let page;
35 |
36 | beforeAll(async function() {
37 | browser = await puppeteer.launch({args: ['--no-sandbox']});
38 | page = await browser.newPage();
39 | });
40 |
41 | afterAll(async function() {
42 | await browser.close();
43 | });
44 |
45 | describe('checkPublicAPI', function() {
46 | it('diff-classes', testLint);
47 | it('diff-methods', testLint);
48 | it('diff-properties', testLint);
49 | it('diff-arguments', testLint);
50 | it('diff-events', testLint);
51 | it('check-duplicates', testLint);
52 | it('check-sorting', testLint);
53 | it('check-returns', testLint);
54 | it('js-builder-common', testJSBuilder);
55 | it('js-builder-inheritance', testJSBuilder);
56 | it('md-builder-common', testMDBuilder);
57 | });
58 |
59 | runner.run();
60 |
61 | async function testLint(state, test) {
62 | const dirPath = path.join(__dirname, test.name);
63 | const {expect} = new Matchers({
64 | toBeGolden: GoldenUtils.compare.bind(null, dirPath, dirPath)
65 | });
66 |
67 | const factory = new SourceFactory();
68 | const mdSources = await factory.readdir(dirPath, '.md');
69 | const jsSources = await factory.readdir(dirPath, '.js');
70 | const messages = await checkPublicAPI(page, mdSources, jsSources);
71 | const errors = messages.map(message => message.text);
72 | expect(errors.join('\n')).toBeGolden('result.txt');
73 | }
74 |
75 | async function testMDBuilder(state, test) {
76 | const dirPath = path.join(__dirname, test.name);
77 | const {expect} = new Matchers({
78 | toBeGolden: GoldenUtils.compare.bind(null, dirPath, dirPath)
79 | });
80 | const factory = new SourceFactory();
81 | const sources = await factory.readdir(dirPath, '.md');
82 | const {documentation} = await mdBuilder(page, sources);
83 | expect(serialize(documentation)).toBeGolden('result.txt');
84 | }
85 |
86 | async function testJSBuilder(state, test) {
87 | const dirPath = path.join(__dirname, test.name);
88 | const {expect} = new Matchers({
89 | toBeGolden: GoldenUtils.compare.bind(null, dirPath, dirPath)
90 | });
91 | const factory = new SourceFactory();
92 | const sources = await factory.readdir(dirPath, '.js');
93 | const {documentation} = await jsBuilder(sources);
94 | expect(serialize(documentation)).toBeGolden('result.txt');
95 | }
96 |
97 | function serialize(doc) {
98 | const result = {classes: []};
99 | for (let cls of doc.classesArray) {
100 | const classJSON = {
101 | name: cls.name,
102 | members: []
103 | };
104 | result.classes.push(classJSON);
105 | for (let member of cls.membersArray) {
106 | classJSON.members.push({
107 | name: member.name,
108 | type: member.type,
109 | hasReturn: member.hasReturn,
110 | async: member.async,
111 | args: member.argsArray.map(arg => arg.name)
112 | });
113 | }
114 | }
115 | return JSON.stringify(result, null, 2);
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/utils/node6-transform/test/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | const transformAsyncFunctions = require('../TransformAsyncFunctions');
17 |
18 | const {TestRunner, Reporter, Matchers} = require('../../testrunner/');
19 | const runner = new TestRunner();
20 | new Reporter(runner);
21 |
22 | const {describe, xdescribe, fdescribe} = runner;
23 | const {it, fit, xit} = runner;
24 | const {beforeAll, beforeEach, afterAll, afterEach} = runner;
25 |
26 | const {expect} = new Matchers();
27 |
28 | describe('TransformAsyncFunctions', function() {
29 | it('should convert a function expression', function(done) {
30 | const input = `(async function(){ return 123 })()`;
31 | const output = eval(transformAsyncFunctions(input));
32 | expect(output instanceof Promise).toBe(true);
33 | output.then(result => expect(result).toBe(123)).then(done);
34 | });
35 | it('should convert an arrow function', function(done) {
36 | const input = `(async () => 123)()`;
37 | const output = eval(transformAsyncFunctions(input));
38 | expect(output instanceof Promise).toBe(true);
39 | output.then(result => expect(result).toBe(123)).then(done);
40 | });
41 | it('should convert an arrow function with curly braces', function(done) {
42 | const input = `(async () => { return 123 })()`;
43 | const output = eval(transformAsyncFunctions(input));
44 | expect(output instanceof Promise).toBe(true);
45 | output.then(result => expect(result).toBe(123)).then(done);
46 | });
47 | it('should convert a function declaration', function(done) {
48 | const input = `async function f(){ return 123; } f();`;
49 | const output = eval(transformAsyncFunctions(input));
50 | expect(output instanceof Promise).toBe(true);
51 | output.then(result => expect(result).toBe(123)).then(done);
52 | });
53 | it('should convert await', function(done) {
54 | const input = `async function f(){ return 23 + await Promise.resolve(100); } f();`;
55 | const output = eval(transformAsyncFunctions(input));
56 | expect(output instanceof Promise).toBe(true);
57 | output.then(result => expect(result).toBe(123)).then(done);
58 | });
59 | it('should convert method', function(done) {
60 | const input = `class X{async f() { return 123 }} (new X()).f();`;
61 | const output = eval(transformAsyncFunctions(input));
62 | expect(output instanceof Promise).toBe(true);
63 | output.then(result => expect(result).toBe(123)).then(done);
64 | });
65 | it('should pass arguments', function(done) {
66 | const input = `(async function(a, b){ return await a + await b })(Promise.resolve(100), 23)`;
67 | const output = eval(transformAsyncFunctions(input));
68 | expect(output instanceof Promise).toBe(true);
69 | output.then(result => expect(result).toBe(123)).then(done);
70 | });
71 | it('should still work across eval', function(done) {
72 | const input = `var str = (async function(){ return 123; }).toString(); eval('(' + str + ')')();`;
73 | const output = eval(transformAsyncFunctions(input));
74 | expect(output instanceof Promise).toBe(true);
75 | output.then(result => expect(result).toBe(123)).then(done);
76 | });
77 | it('should work with double await', function(done) {
78 | const input = `async function f(){ return 23 + await Promise.resolve(50 + await Promise.resolve(50)); } f();`;
79 | const output = eval(transformAsyncFunctions(input));
80 | expect(output instanceof Promise).toBe(true);
81 | output.then(result => expect(result).toBe(123)).then(done);
82 | });
83 | it('should work paren around arrow function', function(done) {
84 | const input = `(async x => ( 123))()`;
85 | const output = eval(transformAsyncFunctions(input));
86 | expect(output instanceof Promise).toBe(true);
87 | output.then(result => expect(result).toBe(123)).then(done);
88 | });
89 | });
90 |
91 | runner.run();
92 |
--------------------------------------------------------------------------------
/utils/node6-transform/TransformAsyncFunctions.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const esprima = require('esprima');
18 | const ESTreeWalker = require('../ESTreeWalker');
19 |
20 | // This is converted from Babel's "transform-async-to-generator"
21 | // https://babeljs.io/docs/plugins/transform-async-to-generator/
22 | const asyncToGenerator = fn => {
23 | const gen = fn.call(this);
24 | return new Promise((resolve, reject) => {
25 | function step(key, arg) {
26 | let info, value;
27 | try {
28 | info = gen[key](arg);
29 | value = info.value;
30 | } catch (error) {
31 | reject(error);
32 | return;
33 | }
34 | if (info.done) {
35 | resolve(value);
36 | } else {
37 | return Promise.resolve(value).then(
38 | value => {
39 | step('next', value);
40 | },
41 | err => {
42 | step('throw', err);
43 | });
44 | }
45 | }
46 | return step('next');
47 | });
48 | };
49 |
50 | /**
51 | * @param {string} text
52 | * @return {string}
53 | */
54 | function transformAsyncFunctions(text) {
55 | const edits = [];
56 |
57 | const ast = esprima.parseScript(text, {range: true, tolerant: true});
58 | const walker = new ESTreeWalker(node => {
59 | if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression')
60 | onFunction(node);
61 | else if (node.type === 'AwaitExpression')
62 | onAwait(node);
63 | });
64 | walker.walk(ast);
65 |
66 | edits.sort((a, b) => b.from - a.from);
67 | for (const {replacement, from, to} of edits)
68 | text = text.substring(0, from) + replacement + text.substring(to);
69 |
70 | return text;
71 |
72 | /**
73 | * @param {ESTree.Node} node
74 | */
75 | function onFunction(node) {
76 | if (!node.async) return;
77 |
78 | let range;
79 | if (node.parent.type === 'MethodDefinition')
80 | range = node.parent.range;
81 | else
82 | range = node.range;
83 | const index = text.substring(range[0], range[1]).indexOf('async') + range[0];
84 | insertText(index, index + 'async'.length, '/* async */');
85 |
86 | let before = `{return (${asyncToGenerator.toString()})(function*()`;
87 | let after = `);}`;
88 | if (node.body.type !== 'BlockStatement') {
89 | before += `{ return `;
90 | after = `; }` + after;
91 |
92 | // Remove parentheses that might wrap an arrow function
93 | const beforeBody = text.substring(node.range[0], node.body.range[0]);
94 | if (/\(\s*$/.test(beforeBody)) {
95 | const afterBody = text.substring(node.body.range[1], node.range[1]);
96 | const openParen = node.range[0] + beforeBody.lastIndexOf('(');
97 | insertText(openParen, openParen + 1, ' ');
98 | const closeParen = node.body.range[1] + afterBody.indexOf(')');
99 | insertText(closeParen, closeParen + 1, ' ');
100 | }
101 | }
102 |
103 |
104 | insertText(node.body.range[0], node.body.range[0], before);
105 | insertText(node.body.range[1], node.body.range[1], after);
106 | }
107 |
108 | /**
109 | * @param {ESTree.Node} node
110 | */
111 | function onAwait(node) {
112 | const index = text.substring(node.range[0], node.range[1]).indexOf('await') + node.range[0];
113 | insertText(index, index + 'await'.length, '(yield');
114 | insertText(node.range[1], node.range[1], ')');
115 | }
116 |
117 | /**
118 | * @param {number} from
119 | * @param {number} to
120 | * @param {string} replacement
121 | */
122 | function insertText(from, to, replacement) {
123 | edits.push({from, to, replacement});
124 | }
125 | }
126 |
127 | module.exports = transformAsyncFunctions;
--------------------------------------------------------------------------------
/install.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | buildNode6IfNecessary();
18 |
19 | if (process.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD) {
20 | console.log('**INFO** Skipping Chromium download. "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" environment variable was found.');
21 | return;
22 | }
23 | if (process.env.NPM_CONFIG_PUPPETEER_SKIP_CHROMIUM_DOWNLOAD || process.env.npm_config_puppeteer_skip_chromium_download) {
24 | console.log('**INFO** Skipping Chromium download. "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" was set in npm config.');
25 | return;
26 | }
27 |
28 | const Downloader = require('./lib/Downloader');
29 | const downloader = Downloader.createDefault();
30 |
31 | const platform = downloader.currentPlatform();
32 | const revision = Downloader.defaultRevision();
33 | const ProgressBar = require('progress');
34 |
35 | const revisionInfo = downloader.revisionInfo(platform, revision);
36 | // Do nothing if the revision is already downloaded.
37 | if (revisionInfo.downloaded)
38 | return;
39 |
40 | // Override current environment proxy settings with npm configuration, if any.
41 | const NPM_HTTPS_PROXY = process.env.npm_config_https_proxy || process.env.npm_config_proxy;
42 | const NPM_HTTP_PROXY = process.env.npm_config_http_proxy || process.env.npm_config_proxy;
43 | const NPM_NO_PROXY = process.env.npm_config_no_proxy;
44 |
45 | if (NPM_HTTPS_PROXY)
46 | process.env.HTTPS_PROXY = NPM_HTTPS_PROXY;
47 | if (NPM_HTTP_PROXY)
48 | process.env.HTTP_PROXY = NPM_HTTP_PROXY;
49 | if (NPM_NO_PROXY)
50 | process.env.NO_PROXY = NPM_NO_PROXY;
51 |
52 | const allRevisions = downloader.downloadedRevisions();
53 | const downloadHost = process.env.PUPPETEER_DOWNLOAD_HOST || process.env.npm_config_puppeteer_download_host;
54 | if (downloadHost)
55 | downloader.setDownloadHost(downloadHost);
56 | downloader.downloadRevision(platform, revision, onProgress)
57 | .then(onSuccess)
58 | .catch(onError);
59 |
60 | /**
61 | * @return {!Promise}
62 | */
63 | function onSuccess() {
64 | console.log('Chromium downloaded to ' + revisionInfo.folderPath);
65 | // Remove previous chromium revisions.
66 | const cleanupOldVersions = allRevisions.map(({platform, revision}) => downloader.removeRevision(platform, revision));
67 | return Promise.all(cleanupOldVersions);
68 | }
69 |
70 | /**
71 | * @param {!Error} error
72 | */
73 | function onError(error) {
74 | console.error(`ERROR: Failed to download Chromium r${revision}! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download.`);
75 | console.error(error);
76 | process.exit(1);
77 | }
78 |
79 | let progressBar = null;
80 | function onProgress(bytesTotal, delta) {
81 | if (!progressBar) {
82 | progressBar = new ProgressBar(`Downloading Chromium r${revision} - ${toMegabytes(bytesTotal)} [:bar] :percent :etas `, {
83 | complete: '=',
84 | incomplete: ' ',
85 | width: 20,
86 | total: bytesTotal,
87 | });
88 | }
89 | progressBar.tick(delta);
90 | }
91 |
92 | function toMegabytes(bytes) {
93 | const mb = bytes / 1024 / 1024;
94 | return `${Math.round(mb * 10) / 10} Mb`;
95 | }
96 |
97 | function buildNode6IfNecessary() {
98 | const fs = require('fs');
99 | const path = require('path');
100 |
101 | // if this package is installed from NPM, then it already has up-to-date node6
102 | // folder.
103 | if (!fs.existsSync(path.join('utils', 'node6-transform')))
104 | return;
105 | let asyncawait = true;
106 | try {
107 | new Function('async function test(){await 1}');
108 | } catch (error) {
109 | asyncawait = false;
110 | }
111 | // if async/await is supported, then node6 is not needed.
112 | if (asyncawait)
113 | return;
114 | // Re-build node6/ folder.
115 | console.log('Building Puppeteer for Node 6');
116 | require(path.join(__dirname, 'utils', 'node6-transform'));
117 | }
118 |
--------------------------------------------------------------------------------
/utils/doclint/SourceFactory.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const path = require('path');
18 | const fs = require('fs');
19 |
20 | const readFileAsync = promisify(fs.readFile);
21 | const readdirAsync = promisify(fs.readdir);
22 | const writeFileAsync = promisify(fs.writeFile);
23 |
24 | const PROJECT_DIR = path.join(__dirname, '..', '..');
25 |
26 | class Source {
27 | /**
28 | * @param {string} filePath
29 | * @param {string} text
30 | */
31 | constructor(filePath, text) {
32 | this._filePath = filePath;
33 | this._projectPath = path.relative(PROJECT_DIR, filePath);
34 | this._name = path.basename(filePath);
35 | this._text = text;
36 | this._hasUpdatedText = false;
37 | }
38 |
39 | /**
40 | * @return {string}
41 | */
42 | filePath() {
43 | return this._filePath;
44 | }
45 |
46 | /**
47 | * @return {string}
48 | */
49 | projectPath() {
50 | return this._projectPath;
51 | }
52 |
53 | /**
54 | * @return {string}
55 | */
56 | name() {
57 | return this._name;
58 | }
59 |
60 | /**
61 | * @param {string} text
62 | * @return {boolean}
63 | */
64 | setText(text) {
65 | if (text === this._text)
66 | return false;
67 | this._hasUpdatedText = true;
68 | this._text = text;
69 | return true;
70 | }
71 |
72 | /**
73 | * @return {string}
74 | */
75 | text() {
76 | return this._text;
77 | }
78 |
79 | /**
80 | * @return {boolean}
81 | */
82 | hasUpdatedText() {
83 | return this._hasUpdatedText;
84 | }
85 | }
86 |
87 | class SourceFactory {
88 | constructor() {
89 | this._sources = new Map();
90 | }
91 |
92 | /**
93 | * @return {!Array}
94 | */
95 | sources() {
96 | return Array.from(this._sources.values());
97 | }
98 |
99 | /**
100 | * @return {!Promise}
101 | */
102 | async saveChangedSources() {
103 | const changedSources = Array.from(this._sources.values()).filter(source => source.hasUpdatedText());
104 | if (!changedSources.length)
105 | return false;
106 | await Promise.all(changedSources.map(source => writeFileAsync(source.filePath(), source.text())));
107 | return true;
108 | }
109 |
110 | /**
111 | * @param {string} filePath
112 | * @return {!Promise}
113 | */
114 | async readFile(filePath) {
115 | filePath = path.resolve(filePath);
116 | let source = this._sources.get(filePath);
117 | if (!source) {
118 | const text = await readFileAsync(filePath, {encoding: 'utf8'});
119 | source = new Source(filePath, text);
120 | this._sources.set(filePath, source);
121 | }
122 | return source;
123 | }
124 |
125 | /**
126 | * @param {string} dirPath
127 | * @param {string=} extension
128 | * @return {!Promise>}
129 | */
130 | async readdir(dirPath, extension = '') {
131 | const fileNames = await readdirAsync(dirPath);
132 | const filePaths = fileNames.filter(fileName => fileName.endsWith(extension)).map(fileName => path.join(dirPath, fileName));
133 | return Promise.all(filePaths.map(filePath => this.readFile(filePath)));
134 | }
135 |
136 | /**
137 | * @param {string} filePath
138 | * @param {string} text
139 | * @return {!Source}
140 | */
141 | createForTest(filePath, text) {
142 | return new Source(filePath, text);
143 | }
144 | }
145 |
146 | /**
147 | * @param {function(?)} nodeFunction
148 | * @return {function(?):!Promise>}
149 | */
150 | function promisify(nodeFunction) {
151 | /**
152 | * @param {!Array>} options
153 | * @return {!Promise>}
154 | */
155 | return function(...options) {
156 | return new Promise(function(fulfill, reject) {
157 | options.push(callback);
158 | nodeFunction.call(null, ...options);
159 | function callback(err, result) {
160 | if (err)
161 | reject(err);
162 | else
163 | fulfill(result);
164 | }
165 | });
166 | };
167 | }
168 |
169 | module.exports = SourceFactory;
170 |
--------------------------------------------------------------------------------
/lib/NavigatorWatcher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const {helper} = require('./helper');
18 | const {FrameManager} = require('./FrameManager');
19 |
20 | class NavigatorWatcher {
21 | /**
22 | * @param {!FrameManager} frameManager
23 | * @param {!Puppeteer.Frame} frame
24 | * @param {number} timeout
25 | * @param {!Object=} options
26 | */
27 | constructor(frameManager, frame, timeout, options = {}) {
28 | console.assert(options.networkIdleTimeout === undefined, 'ERROR: networkIdleTimeout option is no longer supported.');
29 | console.assert(options.networkIdleInflight === undefined, 'ERROR: networkIdleInflight option is no longer supported.');
30 | console.assert(options.waitUntil !== 'networkidle', 'ERROR: "networkidle" option is no longer supported. Use "networkidle2" instead');
31 | let waitUntil = ['load'];
32 | if (Array.isArray(options.waitUntil))
33 | waitUntil = options.waitUntil.slice();
34 | else if (typeof options.waitUntil === 'string')
35 | waitUntil = [options.waitUntil];
36 | this._expectedLifecycle = waitUntil.map(value => {
37 | const protocolEvent = puppeteerToProtocolLifecycle[value];
38 | console.assert(protocolEvent, 'Unknown value for options.waitUntil: ' + value);
39 | return protocolEvent;
40 | });
41 |
42 | this._frameManager = frameManager;
43 | this._frame = frame;
44 | this._initialLoaderId = frame._loaderId;
45 | this._timeout = timeout;
46 | this._eventListeners = [
47 | helper.addEventListener(this._frameManager, FrameManager.Events.LifecycleEvent, this._checkLifecycleComplete.bind(this)),
48 | helper.addEventListener(this._frameManager, FrameManager.Events.FrameDetached, this._checkLifecycleComplete.bind(this))
49 | ];
50 |
51 | const lifecycleCompletePromise = new Promise(fulfill => {
52 | this._lifecycleCompleteCallback = fulfill;
53 | });
54 | this._navigationPromise = Promise.race([
55 | this._createTimeoutPromise(),
56 | lifecycleCompletePromise
57 | ]).then(error => {
58 | this._cleanup();
59 | return error;
60 | });
61 | }
62 |
63 | /**
64 | * @return {!Promise}
65 | */
66 | _createTimeoutPromise() {
67 | if (!this._timeout)
68 | return new Promise(() => {});
69 | const errorMessage = 'Navigation Timeout Exceeded: ' + this._timeout + 'ms exceeded';
70 | return new Promise(fulfill => this._maximumTimer = setTimeout(fulfill, this._timeout))
71 | .then(() => new Error(errorMessage));
72 | }
73 |
74 | /**
75 | * @return {!Promise}
76 | */
77 | async navigationPromise() {
78 | return this._navigationPromise;
79 | }
80 |
81 | _checkLifecycleComplete() {
82 | // We expect navigation to commit.
83 | if (this._frame._loaderId === this._initialLoaderId)
84 | return;
85 | if (!checkLifecycle(this._frame, this._expectedLifecycle))
86 | return;
87 | this._lifecycleCompleteCallback();
88 |
89 | /**
90 | * @param {!Puppeteer.Frame} frame
91 | * @param {!Array} expectedLifecycle
92 | * @return {boolean}
93 | */
94 | function checkLifecycle(frame, expectedLifecycle) {
95 | for (const event of expectedLifecycle) {
96 | if (!frame._lifecycleEvents.has(event))
97 | return false;
98 | }
99 | for (const child of frame.childFrames()) {
100 | if (!checkLifecycle(child, expectedLifecycle))
101 | return false;
102 | }
103 | return true;
104 | }
105 | }
106 |
107 | cancel() {
108 | this._cleanup();
109 | }
110 |
111 | _cleanup() {
112 | helper.removeEventListeners(this._eventListeners);
113 | this._lifecycleCompleteCallback(new Error('Navigation failed'));
114 | clearTimeout(this._maximumTimer);
115 | }
116 | }
117 |
118 | const puppeteerToProtocolLifecycle = {
119 | 'load': 'load',
120 | 'domcontentloaded': 'DOMContentLoaded',
121 | 'networkidle0': 'networkIdle',
122 | 'networkidle2': 'networkAlmostIdle',
123 | };
124 |
125 | module.exports = NavigatorWatcher;
126 |
--------------------------------------------------------------------------------
/utils/testrunner/Reporter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const RED_COLOR = '\x1b[31m';
18 | const GREEN_COLOR = '\x1b[32m';
19 | const YELLOW_COLOR = '\x1b[33m';
20 | const RESET_COLOR = '\x1b[0m';
21 |
22 | class Reporter {
23 | constructor(runner) {
24 | this._runner = runner;
25 | runner.on('started', this._onStarted.bind(this));
26 | runner.on('terminated', this._onTerminated.bind(this));
27 | runner.on('finished', this._onFinished.bind(this));
28 | runner.on('teststarted', this._onTestStarted.bind(this));
29 | runner.on('testfinished', this._onTestFinished.bind(this));
30 | }
31 |
32 | _onStarted() {
33 | this._timestamp = Date.now();
34 | console.log(`Running ${YELLOW_COLOR}${this._runner.parallel()}${RESET_COLOR} worker(s):\n`);
35 | }
36 |
37 | _onTerminated(message, error) {
38 | this._printTestResults();
39 | console.log(`${RED_COLOR}## TERMINATED ##${RESET_COLOR}`);
40 | console.log('Message:');
41 | console.log(` ${RED_COLOR}${message}${RESET_COLOR}`);
42 | if (error && error.stack) {
43 | console.log('Stack:');
44 | console.log(error.stack.split('\n').map(line => ' ' + line).join('\n'));
45 | }
46 | process.exit(2);
47 | }
48 |
49 | _onFinished() {
50 | this._printTestResults();
51 | const failedTests = this._runner.failedTests();
52 | process.exit(failedTests.length > 0 ? 1 : 0);
53 | }
54 |
55 | _printTestResults() {
56 | // 2 newlines after completing all tests.
57 | console.log('\n');
58 |
59 | const failedTests = this._runner.failedTests();
60 | if (failedTests.length > 0) {
61 | console.log('\nFailures:');
62 | for (let i = 0; i < failedTests.length; ++i) {
63 | const test = failedTests[i];
64 | console.log(`${i + 1}) ${test.fullName}`);
65 | if (test.result === 'timedout') {
66 | console.log(' Message:');
67 | console.log(` ${YELLOW_COLOR}Timeout Exceeded ${this._runner.timeout()}ms${RESET_COLOR} ${formatLocation(test)}`);
68 | } else {
69 | console.log(' Message:');
70 | console.log(` ${RED_COLOR}${test.error.message || test.error}${RESET_COLOR} ${formatLocation(test)}`);
71 | console.log(' Stack:');
72 | if (test.error.stack)
73 | console.log(test.error.stack.split('\n').map(line => ' ' + line).join('\n'));
74 | }
75 | console.log('');
76 | }
77 | }
78 |
79 | const tests = this._runner.tests();
80 | const skippedTests = tests.filter(test => test.result === 'skipped');
81 | if (skippedTests.length > 0) {
82 | console.log('\nSkipped:');
83 | for (let i = 0; i < skippedTests.length; ++i) {
84 | const test = skippedTests[i];
85 | console.log(`${i + 1}) ${test.fullName}`);
86 | console.log(` ${YELLOW_COLOR}Temporary disabled with xit${RESET_COLOR} ${formatLocation(test)}\n`);
87 | }
88 | }
89 |
90 | const executedTests = tests.filter(test => test.result);
91 | console.log(`\nRan ${executedTests.length} of ${tests.length} test(s)`);
92 | const milliseconds = Date.now() - this._timestamp;
93 | const seconds = milliseconds / 1000;
94 | console.log(`Finished in ${YELLOW_COLOR}${seconds}${RESET_COLOR} seconds`);
95 |
96 | function formatLocation(test) {
97 | const location = test.location;
98 | if (!location)
99 | return '';
100 | return `@ ${location.fileName}:${location.lineNumber}:${location.columnNumber}`;
101 | }
102 | }
103 |
104 | _onTestStarted() {
105 | }
106 |
107 | _onTestFinished(test) {
108 | if (test.result === 'ok')
109 | process.stdout.write(`${GREEN_COLOR}.${RESET_COLOR}`);
110 | else if (test.result === 'skipped')
111 | process.stdout.write(`${YELLOW_COLOR}*${RESET_COLOR}`);
112 | else if (test.result === 'failed')
113 | process.stdout.write(`${RED_COLOR}F${RESET_COLOR}`);
114 | else if (test.result === 'timedout')
115 | process.stdout.write(`${RED_COLOR}T${RESET_COLOR}`);
116 | }
117 | }
118 |
119 | module.exports = Reporter;
120 |
--------------------------------------------------------------------------------
/utils/ESTreeWalker.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | /**
6 | * @unrestricted
7 | */
8 | class ESTreeWalker {
9 | /**
10 | * @param {function(!ESTree.Node):(!Object|undefined)} beforeVisit
11 | * @param {function(!ESTree.Node)=} afterVisit
12 | */
13 | constructor(beforeVisit, afterVisit) {
14 | this._beforeVisit = beforeVisit;
15 | this._afterVisit = afterVisit || new Function();
16 | }
17 |
18 | /**
19 | * @param {!ESTree.Node} ast
20 | */
21 | walk(ast) {
22 | this._innerWalk(ast, null);
23 | }
24 |
25 | /**
26 | * @param {!ESTree.Node} node
27 | * @param {?ESTree.Node} parent
28 | */
29 | _innerWalk(node, parent) {
30 | if (!node)
31 | return;
32 | node.parent = parent;
33 |
34 | if (this._beforeVisit.call(null, node) === ESTreeWalker.SkipSubtree) {
35 | this._afterVisit.call(null, node);
36 | return;
37 | }
38 |
39 | const walkOrder = ESTreeWalker._walkOrder[node.type];
40 | if (!walkOrder)
41 | return;
42 |
43 | if (node.type === 'TemplateLiteral') {
44 | const templateLiteral = /** @type {!ESTree.TemplateLiteralNode} */ (node);
45 | const expressionsLength = templateLiteral.expressions.length;
46 | for (let i = 0; i < expressionsLength; ++i) {
47 | this._innerWalk(templateLiteral.quasis[i], templateLiteral);
48 | this._innerWalk(templateLiteral.expressions[i], templateLiteral);
49 | }
50 | this._innerWalk(templateLiteral.quasis[expressionsLength], templateLiteral);
51 | } else {
52 | for (let i = 0; i < walkOrder.length; ++i) {
53 | const entity = node[walkOrder[i]];
54 | if (Array.isArray(entity))
55 | this._walkArray(entity, node);
56 | else
57 | this._innerWalk(entity, node);
58 | }
59 | }
60 |
61 | this._afterVisit.call(null, node);
62 | }
63 |
64 | /**
65 | * @param {!Array.} nodeArray
66 | * @param {?ESTree.Node} parentNode
67 | */
68 | _walkArray(nodeArray, parentNode) {
69 | for (let i = 0; i < nodeArray.length; ++i)
70 | this._innerWalk(nodeArray[i], parentNode);
71 | }
72 | }
73 |
74 | /** @typedef {!Object} ESTreeWalker.SkipSubtree */
75 | ESTreeWalker.SkipSubtree = {};
76 |
77 | /** @enum {!Array.} */
78 | ESTreeWalker._walkOrder = {
79 | 'AwaitExpression': ['argument'],
80 | 'ArrayExpression': ['elements'],
81 | 'ArrowFunctionExpression': ['params', 'body'],
82 | 'AssignmentExpression': ['left', 'right'],
83 | 'BinaryExpression': ['left', 'right'],
84 | 'BlockStatement': ['body'],
85 | 'BreakStatement': ['label'],
86 | 'CallExpression': ['callee', 'arguments'],
87 | 'CatchClause': ['param', 'body'],
88 | 'ClassBody': ['body'],
89 | 'ClassDeclaration': ['id', 'superClass', 'body'],
90 | 'ClassExpression': ['id', 'superClass', 'body'],
91 | 'ConditionalExpression': ['test', 'consequent', 'alternate'],
92 | 'ContinueStatement': ['label'],
93 | 'DebuggerStatement': [],
94 | 'DoWhileStatement': ['body', 'test'],
95 | 'EmptyStatement': [],
96 | 'ExpressionStatement': ['expression'],
97 | 'ForInStatement': ['left', 'right', 'body'],
98 | 'ForOfStatement': ['left', 'right', 'body'],
99 | 'ForStatement': ['init', 'test', 'update', 'body'],
100 | 'FunctionDeclaration': ['id', 'params', 'body'],
101 | 'FunctionExpression': ['id', 'params', 'body'],
102 | 'Identifier': [],
103 | 'IfStatement': ['test', 'consequent', 'alternate'],
104 | 'LabeledStatement': ['label', 'body'],
105 | 'Literal': [],
106 | 'LogicalExpression': ['left', 'right'],
107 | 'MemberExpression': ['object', 'property'],
108 | 'MethodDefinition': ['key', 'value'],
109 | 'NewExpression': ['callee', 'arguments'],
110 | 'ObjectExpression': ['properties'],
111 | 'ObjectPattern': ['properties'],
112 | 'ParenthesizedExpression': ['expression'],
113 | 'Program': ['body'],
114 | 'Property': ['key', 'value'],
115 | 'ReturnStatement': ['argument'],
116 | 'SequenceExpression': ['expressions'],
117 | 'Super': [],
118 | 'SwitchCase': ['test', 'consequent'],
119 | 'SwitchStatement': ['discriminant', 'cases'],
120 | 'TaggedTemplateExpression': ['tag', 'quasi'],
121 | 'TemplateElement': [],
122 | 'TemplateLiteral': ['quasis', 'expressions'],
123 | 'ThisExpression': [],
124 | 'ThrowStatement': ['argument'],
125 | 'TryStatement': ['block', 'handler', 'finalizer'],
126 | 'UnaryExpression': ['argument'],
127 | 'UpdateExpression': ['argument'],
128 | 'VariableDeclaration': ['declarations'],
129 | 'VariableDeclarator': ['id', 'init'],
130 | 'WhileStatement': ['test', 'body'],
131 | 'WithStatement': ['object', 'body'],
132 | 'YieldExpression': ['argument']
133 | };
134 |
135 | module.exports = ESTreeWalker;
136 |
--------------------------------------------------------------------------------
/test/assets/csscoverage/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011, Edgar Tolentino and Pablo Impallari (www.impallari.com|impallari@gmail.com),
2 | Copyright (c) 2011, Igino Marini. (www.ikern.com|mail@iginomarini.com),
3 | with Reserved Font Names "Dosis".
4 |
5 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
6 | This license is copied below, and is also available with a FAQ at:
7 | http://scripts.sil.org/OFL
8 |
9 |
10 | -----------------------------------------------------------
11 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
12 | -----------------------------------------------------------
13 |
14 | PREAMBLE
15 | The goals of the Open Font License (OFL) are to stimulate worldwide
16 | development of collaborative font projects, to support the font creation
17 | efforts of academic and linguistic communities, and to provide a free and
18 | open framework in which fonts may be shared and improved in partnership
19 | with others.
20 |
21 | The OFL allows the licensed fonts to be used, studied, modified and
22 | redistributed freely as long as they are not sold by themselves. The
23 | fonts, including any derivative works, can be bundled, embedded,
24 | redistributed and/or sold with any software provided that any reserved
25 | names are not used by derivative works. The fonts and derivatives,
26 | however, cannot be released under any other type of license. The
27 | requirement for fonts to remain under this license does not apply
28 | to any document created using the fonts or their derivatives.
29 |
30 | DEFINITIONS
31 | "Font Software" refers to the set of files released by the Copyright
32 | Holder(s) under this license and clearly marked as such. This may
33 | include source files, build scripts and documentation.
34 |
35 | "Reserved Font Name" refers to any names specified as such after the
36 | copyright statement(s).
37 |
38 | "Original Version" refers to the collection of Font Software components as
39 | distributed by the Copyright Holder(s).
40 |
41 | "Modified Version" refers to any derivative made by adding to, deleting,
42 | or substituting -- in part or in whole -- any of the components of the
43 | Original Version, by changing formats or by porting the Font Software to a
44 | new environment.
45 |
46 | "Author" refers to any designer, engineer, programmer, technical
47 | writer or other person who contributed to the Font Software.
48 |
49 | PERMISSION & CONDITIONS
50 | Permission is hereby granted, free of charge, to any person obtaining
51 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
52 | redistribute, and sell modified and unmodified copies of the Font
53 | Software, subject to the following conditions:
54 |
55 | 1) Neither the Font Software nor any of its individual components,
56 | in Original or Modified Versions, may be sold by itself.
57 |
58 | 2) Original or Modified Versions of the Font Software may be bundled,
59 | redistributed and/or sold with any software, provided that each copy
60 | contains the above copyright notice and this license. These can be
61 | included either as stand-alone text files, human-readable headers or
62 | in the appropriate machine-readable metadata fields within text or
63 | binary files as long as those fields can be easily viewed by the user.
64 |
65 | 3) No Modified Version of the Font Software may use the Reserved Font
66 | Name(s) unless explicit written permission is granted by the corresponding
67 | Copyright Holder. This restriction only applies to the primary font name as
68 | presented to the users.
69 |
70 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
71 | Software shall not be used to promote, endorse or advertise any
72 | Modified Version, except to acknowledge the contribution(s) of the
73 | Copyright Holder(s) and the Author(s) or with their explicit written
74 | permission.
75 |
76 | 5) The Font Software, modified or unmodified, in part or in whole,
77 | must be distributed entirely under this license, and must not be
78 | distributed under any other license. The requirement for fonts to
79 | remain under this license does not apply to any document created
80 | using the Font Software.
81 |
82 | TERMINATION
83 | This license becomes null and void if any of the above conditions are
84 | not met.
85 |
86 | DISCLAIMER
87 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
88 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
89 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
90 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
91 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
92 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
93 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
94 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
95 | OTHER DEALINGS IN THE FONT SOFTWARE.
96 |
--------------------------------------------------------------------------------
/test/golden-utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | const path = require('path');
17 | const fs = require('fs');
18 | const Diff = require('text-diff');
19 | const mime = require('mime');
20 | const PNG = require('pngjs').PNG;
21 | const pixelmatch = require('pixelmatch');
22 |
23 | module.exports = {compare};
24 |
25 | const GoldenComparators = {
26 | 'image/png': compareImages,
27 | 'text/plain': compareText
28 | };
29 |
30 | /**
31 | * @param {?Object} actualBuffer
32 | * @param {!Buffer} expectedBuffer
33 | * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
34 | */
35 | function compareImages(actualBuffer, expectedBuffer) {
36 | if (!actualBuffer || !(actualBuffer instanceof Buffer))
37 | return { errorMessage: 'Actual result should be Buffer.' };
38 |
39 | const actual = PNG.sync.read(actualBuffer);
40 | const expected = PNG.sync.read(expectedBuffer);
41 | if (expected.width !== actual.width || expected.height !== actual.height) {
42 | return {
43 | errorMessage: `Sizes differ: expected image ${expected.width}px X ${expected.height}px, but got ${actual.width}px X ${actual.height}px. `
44 | };
45 | }
46 | const diff = new PNG({width: expected.width, height: expected.height});
47 | const count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.1});
48 | return count > 0 ? { diff: PNG.sync.write(diff) } : null;
49 | }
50 |
51 | /**
52 | * @param {?Object} actual
53 | * @param {!Buffer} expectedBuffer
54 | * @return {?{diff: (!Object:undefined), errorMessage: (string|undefined)}}
55 | */
56 | function compareText(actual, expectedBuffer) {
57 | if (typeof actual !== 'string')
58 | return { errorMessage: 'Actual result should be string' };
59 | const expected = expectedBuffer.toString('utf-8');
60 | if (expected === actual)
61 | return null;
62 | const diff = new Diff();
63 | const result = diff.main(expected, actual);
64 | diff.cleanupSemantic(result);
65 | let html = diff.prettyHtml(result);
66 | const diffStylePath = path.join(__dirname, 'diffstyle.css');
67 | html = `` + html;
68 | return {
69 | diff: html,
70 | diffExtension: '.html'
71 | };
72 | }
73 |
74 | /**
75 | * @param {?Object} actual
76 | * @param {string} goldenName
77 | * @return {!{pass: boolean, message: (undefined|string)}}
78 | */
79 | function compare(goldenPath, outputPath, actual, goldenName) {
80 | goldenPath = path.normalize(goldenPath);
81 | outputPath = path.normalize(outputPath);
82 | const expectedPath = path.join(goldenPath, goldenName);
83 | const actualPath = path.join(outputPath, goldenName);
84 |
85 | const messageSuffix = 'Output is saved in "' + path.basename(outputPath + '" directory');
86 |
87 | if (!fs.existsSync(expectedPath)) {
88 | ensureOutputDir();
89 | fs.writeFileSync(actualPath, actual);
90 | return {
91 | pass: false,
92 | message: goldenName + ' is missing in golden results. ' + messageSuffix
93 | };
94 | }
95 | const expected = fs.readFileSync(expectedPath);
96 | const comparator = GoldenComparators[mime.lookup(goldenName)];
97 | if (!comparator) {
98 | return {
99 | pass: false,
100 | message: 'Failed to find comparator with type ' + mime.lookup(goldenName) + ': ' + goldenName
101 | };
102 | }
103 | const result = comparator(actual, expected);
104 | if (!result)
105 | return { pass: true };
106 | ensureOutputDir();
107 | if (goldenPath === outputPath) {
108 | fs.writeFileSync(addSuffix(actualPath, '-actual'), actual);
109 | } else {
110 | fs.writeFileSync(actualPath, actual);
111 | // Copy expected to the output/ folder for convenience.
112 | fs.writeFileSync(addSuffix(actualPath, '-expected'), expected);
113 | }
114 | if (result.diff) {
115 | const diffPath = addSuffix(actualPath, '-diff', result.diffExtension);
116 | fs.writeFileSync(diffPath, result.diff);
117 | }
118 |
119 | let message = goldenName + ' mismatch!';
120 | if (result.errorMessage)
121 | message += ' ' + result.errorMessage;
122 | return {
123 | pass: false,
124 | message: message + ' ' + messageSuffix
125 | };
126 |
127 | function ensureOutputDir() {
128 | if (!fs.existsSync(outputPath))
129 | fs.mkdirSync(outputPath);
130 | }
131 | }
132 |
133 | /**
134 | * @param {string} filePath
135 | * @param {string} suffix
136 | * @param {string=} customExtension
137 | * @return {string}
138 | */
139 | function addSuffix(filePath, suffix, customExtension) {
140 | const dirname = path.dirname(filePath);
141 | const ext = path.extname(filePath);
142 | const name = path.basename(filePath, ext);
143 | return path.join(dirname, name + suffix + (customExtension || ext));
144 | }
145 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | First of all, thank you for your interest in Puppeteer!
4 | We'd love to accept your patches and contributions!
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution,
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Getting setup
19 |
20 | 1. Clone this repository
21 | ```bash
22 | git clone https://github.com/GoogleChrome/puppeteer
23 | cd puppeteer
24 | ```
25 | 2. Install dependencies
26 | ```bash
27 | yarn # or 'npm install'
28 | ```
29 |
30 | ## Code reviews
31 |
32 | All submissions, including submissions by project members, require review. We
33 | use GitHub pull requests for this purpose. Consult
34 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
35 | information on using pull requests.
36 |
37 | ## Code Style
38 |
39 | - coding style is fully defined in [.eslintrc](https://github.com/GoogleChrome/puppeteer/blob/master/.eslintrc.js)
40 | - code should be annotated with [closure annotations](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler)
41 | - comments should be generally avoided. If the code would not be understood without comments, consider re-writing the code to make it self-explanatory
42 |
43 | To run code linter, use:
44 | ```
45 | npm run lint
46 | ```
47 |
48 | ## Commit Messages
49 |
50 | Commit messages should follow the Semantic Commit Messages format:
51 |
52 | ```
53 | label(namespace): title
54 |
55 | description
56 |
57 | footer
58 | ```
59 |
60 | 1. *label* is one of the following:
61 | - `fix` - puppeteer bug fixes
62 | - `feat` - puppeteer features
63 | - `docs` - changes to docs, e.g. `docs(api.md): ..` to change documentation
64 | - `test` - changes to puppeteer tests infrastructure
65 | - `style` - puppeteer code style: spaces/alignment/wrapping etc
66 | - `chore` - build-related work, e.g. doclint changes / travis / appveyor
67 | 1. *namespace* is put in parenthesis after label and is optional
68 | 2. *title* is a brief summary of changes
69 | 3. *description* is **optional**, new-line separated from title and is in present tense
70 | 4. *footer* is **optional**, new-line separated from *description* and contains "fixes" / "references" attribution to github issues
71 | 5. *footer* should also include "BREAKING CHANGE" if current API clients will break due to this change. It should explain what changed and how to get the old behavior.
72 |
73 | Example:
74 |
75 | ```
76 | fix(Page): fix page.pizza method
77 |
78 | This patch fixes page.pizza so that it works with iframes.
79 |
80 | Fixes #123, Fixes #234
81 |
82 | BREAKING CHANGE: page.pizza now delivers pizza at home by default.
83 | To deliver to a different location, use "deliver" option:
84 | `page.pizza({deliver: 'work'})`.
85 | ```
86 |
87 |
88 | ## Writing Documentation
89 |
90 | All public API should have a descriptive entry in the [docs/api.md](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md). There's a [documentation linter](https://github.com/GoogleChrome/puppeteer/tree/master/utils/doclint) which makes sure documentation is aligned with the codebase.
91 |
92 | To run documentation linter, use
93 | ```
94 | npm run doc
95 | ```
96 |
97 | ## Adding New Dependencies
98 |
99 | For all dependencies (both installation and development):
100 | - **Do not add** a dependency if the desired functionality is easily implementable
101 | - if adding a dependency, it should be well-maintained and trustworthy
102 |
103 | A barrier for introducing new installation dependencies is especially high:
104 | - **do not add** installation dependency unless it's critical to project success
105 |
106 | ## Writing Tests
107 |
108 | - every feature should be accompanied by a test
109 | - every public api event/method should be accompanied by a test
110 | - tests should be *hermetic*. Tests should not depend on external services.
111 | - tests should work on all three platforms: Mac, Linux and Win. This is especially important for screenshot tests.
112 |
113 | Puppeteer tests are located in [test/test.js](https://github.com/GoogleChrome/puppeteer/blob/master/test/test.js)
114 | and are written with a [TestRunner](https://github.com/GoogleChrome/puppeteer/tree/master/utils/testrunner) framework.
115 | Despite being named 'unit', these are integration tests, making sure public API methods and events work as expected.
116 |
117 | - To run all tests:
118 | ```
119 | npm run unit
120 | ```
121 | - To filter tests by name:
122 | ```
123 | npm run unit -- --filter=waitFor
124 | ```
125 | - To run a specific test, substitute the `it` with `fit` (mnemonic rule: '*focus it*'):
126 | ```js
127 | ...
128 | // Using "fit" to run specific test
129 | fit('should work', SX(async function() {
130 | const response = await page.goto(EMPTY_PAGE);
131 | expect(response.ok).toBe(true);
132 | }))
133 | ```
134 | - To disable a specific test, substitute the `it` with `xit` (mnemonic rule: '*cross it*'):
135 | ```js
136 | ...
137 | // Using "xit" to skip specific test
138 | xit('should work', SX(async function() {
139 | const response = await page.goto(EMPTY_PAGE);
140 | expect(response.ok).toBe(true);
141 | }))
142 | ```
143 | - To run tests in non-headless mode:
144 | ```
145 | HEADLESS=false npm run unit
146 | ```
147 | - To run tests with custom Chromium executable:
148 | ```
149 | CHROME= npm run unit
150 | ```
151 | - To run tests in slow-mode:
152 | ```
153 | HEADLESS=false SLOW_MO=500 npm run unit
154 | ```
155 | - To debug a test, "focus" a test first and then run:
156 | ```
157 | npm run debug-unit
158 | ```
159 |
160 | ## Public API Coverage
161 |
162 | Every public API method or event should be called at least once in tests. To ensure this, there's a coverage command which tracks calls to public API and reports back if some methods/events were not called.
163 |
164 | Run coverage:
165 |
166 | ```
167 | npm run coverage
168 | ```
169 |
170 | ## Debugging Puppeteer
171 | See [Debugging Tips](README.md#debugging-tips) in the readme
172 |
173 |
--------------------------------------------------------------------------------
/utils/check_availability.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /**
3 | * Copyright 2017 Google Inc. All rights reserved.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | const Downloader = require('../lib/Downloader');
19 | const https = require('https');
20 | const OMAHA_PROXY = 'https://omahaproxy.appspot.com/all.json';
21 |
22 | const downloader = Downloader.createDefault();
23 |
24 | const colors = {
25 | reset: '\x1b[0m',
26 | red: '\x1b[31m',
27 | green: '\x1b[32m',
28 | yellow: '\x1b[33m'
29 | };
30 |
31 | class Table {
32 | /**
33 | * @param {!Array} columnWidths
34 | */
35 | constructor(columnWidths) {
36 | this.widths = columnWidths;
37 | }
38 |
39 | /**
40 | * @param {!Array} values
41 | */
42 | drawRow(values) {
43 | console.assert(values.length === this.widths.length);
44 | let row = '';
45 | for (let i = 0; i < values.length; ++i)
46 | row += padCenter(values[i], this.widths[i]);
47 | console.log(row);
48 | }
49 | }
50 |
51 | if (process.argv.length === 2) {
52 | checkOmahaProxyAvailability();
53 | return;
54 | }
55 | if (process.argv.length !== 4) {
56 | console.log(`
57 | Usage: node check_revisions.js [fromRevision] [toRevision]
58 |
59 | This script checks availability of different prebuild chromium revisions.
60 | Running command without arguments will check against omahaproxy revisions.`);
61 | return;
62 | }
63 |
64 | const fromRevision = parseInt(process.argv[2], 10);
65 | const toRevision = parseInt(process.argv[3], 10);
66 | checkRangeAvailability(fromRevision, toRevision);
67 |
68 | async function checkOmahaProxyAvailability() {
69 | console.log('Fetching revisions from ' + OMAHA_PROXY);
70 | const platforms = await loadJSON(OMAHA_PROXY);
71 | if (!platforms) {
72 | console.error('ERROR: failed to fetch chromium revisions from omahaproxy.');
73 | return;
74 | }
75 | const table = new Table([27, 7, 7, 7, 7]);
76 | table.drawRow([''].concat(downloader.supportedPlatforms()));
77 | for (const platform of platforms) {
78 | // Trust only to the main platforms.
79 | if (platform.os !== 'mac' && platform.os !== 'win' && platform.os !== 'win64' && platform.os !== 'linux')
80 | continue;
81 | const osName = platform.os === 'win' ? 'win32' : platform.os;
82 | for (const version of platform.versions) {
83 | if (version.channel !== 'dev' && version.channel !== 'beta' && version.channel !== 'canary' && version.channel !== 'stable')
84 | continue;
85 | const revisionName = padLeft('[' + osName + ' ' + version.channel + ']', 15);
86 | const revision = parseInt(version.branch_base_position, 10);
87 | await checkAndDrawRevisionAvailability(table, revisionName, revision);
88 | }
89 | }
90 | }
91 |
92 | /**
93 | * @param {number} fromRevision
94 | * @param {number} toRevision
95 | */
96 | async function checkRangeAvailability(fromRevision, toRevision) {
97 | const table = new Table([10, 7, 7, 7, 7]);
98 | table.drawRow([''].concat(downloader.supportedPlatforms()));
99 | const inc = fromRevision < toRevision ? 1 : -1;
100 | for (let revision = fromRevision; revision !== toRevision; revision += inc)
101 | await checkAndDrawRevisionAvailability(table, '', revision);
102 | }
103 |
104 | /**
105 | * @param {!Table} table
106 | * @param {string} name
107 | * @param {number} revision
108 | */
109 | async function checkAndDrawRevisionAvailability(table, name, revision) {
110 | const promises = [];
111 | for (const platform of downloader.supportedPlatforms())
112 | promises.push(downloader.canDownloadRevision(platform, revision));
113 | const availability = await Promise.all(promises);
114 | const allAvailable = availability.every(e => !!e);
115 | const values = [name + ' ' + (allAvailable ? colors.green + revision + colors.reset : revision)];
116 | for (let i = 0; i < availability.length; ++i) {
117 | const decoration = availability[i] ? '+' : '-';
118 | const color = availability[i] ? colors.green : colors.red;
119 | values.push(color + decoration + colors.reset);
120 | }
121 | table.drawRow(values);
122 | }
123 |
124 | /**
125 | * @param {string} url
126 | * @return {!Promise}
127 | */
128 | function loadJSON(url) {
129 | let resolve;
130 | const promise = new Promise(x => resolve = x);
131 | https.get(url, response => {
132 | if (response.statusCode !== 200) {
133 | resolve(null);
134 | return;
135 | }
136 | let body = '';
137 | response.on('data', function(chunk){
138 | body += chunk;
139 | });
140 | response.on('end', function(){
141 | const json = JSON.parse(body);
142 | resolve(json);
143 | });
144 | }).on('error', function(e){
145 | console.error('Error fetching json: ' + e);
146 | resolve(null);
147 | });
148 | return promise;
149 | }
150 |
151 | /**
152 | * @param {number} size
153 | * @return {string}
154 | */
155 | function spaceString(size) {
156 | return new Array(size).fill(' ').join('');
157 | }
158 |
159 | /**
160 | * @param {string} text
161 | * @return {string}
162 | */
163 | function filterOutColors(text) {
164 | for (const colorName in colors) {
165 | const color = colors[colorName];
166 | text = text.replace(color, '');
167 | }
168 | return text;
169 | }
170 |
171 | /**
172 | * @param {string} text
173 | * @param {number} length
174 | * @return {string}
175 | */
176 | function padLeft(text, length) {
177 | const printableCharacters = filterOutColors(text);
178 | return printableCharacters.length >= length ? text : spaceString(length - text.length) + text;
179 | }
180 |
181 | /**
182 | * @param {string} text
183 | * @param {number} length
184 | * @return {string}
185 | */
186 | function padCenter(text, length) {
187 | const printableCharacters = filterOutColors(text);
188 | if (printableCharacters.length >= length)
189 | return text;
190 | const left = Math.floor((length - printableCharacters.length) / 2);
191 | const right = Math.ceil((length - printableCharacters.length) / 2);
192 | return spaceString(left) + text + spaceString(right);
193 | }
194 |
--------------------------------------------------------------------------------
/test/server/SimpleServer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const http = require('http');
18 | const https = require('https');
19 | const url = require('url');
20 | const fs = require('fs');
21 | const path = require('path');
22 | const mime = require('mime');
23 | const WebSocketServer = require('ws').Server;
24 |
25 | const fulfillSymbol = Symbol('fullfill callback');
26 | const rejectSymbol = Symbol('reject callback');
27 |
28 | class SimpleServer {
29 | /**
30 | * @param {string} dirPath
31 | * @param {number} port
32 | * @return {!SimpleServer}
33 | */
34 | static async create(dirPath, port) {
35 | const server = new SimpleServer(dirPath, port);
36 | await new Promise(x => server._server.once('listening', x));
37 | return server;
38 | }
39 |
40 | /**
41 | * @param {string} dirPath
42 | * @param {number} port
43 | * @return {!SimpleServer}
44 | */
45 | static async createHTTPS(dirPath, port) {
46 | const server = new SimpleServer(dirPath, port, {
47 | key: fs.readFileSync(path.join(__dirname, 'key.pem')),
48 | cert: fs.readFileSync(path.join(__dirname, 'cert.pem')),
49 | passphrase: 'aaaa',
50 | });
51 | await new Promise(x => server._server.once('listening', x));
52 | return server;
53 | }
54 |
55 | /**
56 | * @param {string} dirPath
57 | * @param {number} port
58 | * @param {!Object=} sslOptions
59 | */
60 | constructor(dirPath, port, sslOptions) {
61 | if (sslOptions)
62 | this._server = https.createServer(sslOptions, this._onRequest.bind(this));
63 | else
64 | this._server = http.createServer(this._onRequest.bind(this));
65 | this._server.on('connection', socket => this._onSocket(socket));
66 | this._wsServer = new WebSocketServer({server: this._server});
67 | this._wsServer.on('connection', this._onWebSocketConnection.bind(this));
68 | this._server.listen(port);
69 | this._dirPath = dirPath;
70 |
71 | /** @type {!Set} */
72 | this._sockets = new Set();
73 |
74 | /** @type {!Map} */
75 | this._routes = new Map();
76 | /** @type {!Map} */
77 | this._auths = new Map();
78 | /** @type {!Map} */
79 | this._requestSubscribers = new Map();
80 | }
81 |
82 | _onSocket(socket) {
83 | this._sockets.add(socket);
84 | // ECONNRESET is a legit error given
85 | // that tab closing simply kills process.
86 | socket.on('error', error => {
87 | if (error.code !== 'ECONNRESET')
88 | throw error;
89 | });
90 | socket.once('close', () => this._sockets.delete(socket));
91 | }
92 |
93 | /**
94 | * @param {string} path
95 | * @param {string} username
96 | * @param {string} password
97 | */
98 | setAuth(path, username, password) {
99 | this._auths.set(path, {username, password});
100 | }
101 |
102 | async stop() {
103 | this.reset();
104 | for (const socket of this._sockets)
105 | socket.destroy();
106 | this._sockets.clear();
107 | await new Promise(x => this._server.close(x));
108 | }
109 |
110 | /**
111 | * @param {string} path
112 | * @param {function(!IncomingMessage, !ServerResponse)} handler
113 | */
114 | setRoute(path, handler) {
115 | this._routes.set(path, handler);
116 | }
117 |
118 | /**
119 | * @param {string} fromPath
120 | * @param {string} toPath
121 | */
122 | setRedirect(from, to) {
123 | this.setRoute(from, (req, res) => {
124 | res.writeHead(302, { location: to });
125 | res.end();
126 | });
127 | }
128 |
129 | /**
130 | * @param {string} path
131 | * @return {!Promise}
132 | */
133 | waitForRequest(path) {
134 | let promise = this._requestSubscribers.get(path);
135 | if (promise)
136 | return promise;
137 | let fulfill, reject;
138 | promise = new Promise((f, r) => {
139 | fulfill = f;
140 | reject = r;
141 | });
142 | promise[fulfillSymbol] = fulfill;
143 | promise[rejectSymbol] = reject;
144 | this._requestSubscribers.set(path, promise);
145 | return promise;
146 | }
147 |
148 | reset() {
149 | this._routes.clear();
150 | this._auths.clear();
151 | const error = new Error('Static Server has been reset');
152 | for (const subscriber of this._requestSubscribers.values())
153 | subscriber[rejectSymbol].call(null, error);
154 | this._requestSubscribers.clear();
155 | }
156 |
157 | _onRequest(request, response) {
158 | request.on('error', error => {
159 | if (error.code === 'ECONNRESET')
160 | response.end();
161 | else
162 | throw error;
163 | });
164 | const pathName = url.parse(request.url).path;
165 | if (this._auths.has(pathName)) {
166 | const auth = this._auths.get(pathName);
167 | const credentials = new Buffer((request.headers.authorization || '').split(' ')[1] || '', 'base64').toString();
168 | if (credentials !== `${auth.username}:${auth.password}`) {
169 | response.writeHead(401, { 'WWW-Authenticate': 'Basic realm="Secure Area"' });
170 | response.end('HTTP Error 401 Unauthorized: Access is denied');
171 | return;
172 | }
173 | }
174 | // Notify request subscriber.
175 | if (this._requestSubscribers.has(pathName))
176 | this._requestSubscribers.get(pathName)[fulfillSymbol].call(null, request);
177 | const handler = this._routes.get(pathName);
178 | if (handler)
179 | handler.call(null, request, response);
180 | else
181 | this.defaultHandler(request, response);
182 | }
183 |
184 | /**
185 | * @param {!IncomingMessage} request
186 | * @param {!ServerResponse} response
187 | */
188 | defaultHandler(request, response) {
189 | let pathName = url.parse(request.url).path;
190 | if (pathName === '/')
191 | pathName = '/index.html';
192 | pathName = path.join(this._dirPath, pathName.substring(1));
193 |
194 | fs.readFile(pathName, function(err, data) {
195 | if (err) {
196 | response.statusCode = 404;
197 | response.end(`File not found: ${pathName}`);
198 | return;
199 | }
200 | response.setHeader('Content-Type', mime.lookup(pathName));
201 | response.end(data);
202 | });
203 | }
204 |
205 | _onWebSocketConnection(connection) {
206 | connection.send('opened');
207 | }
208 | }
209 |
210 | module.exports = SimpleServer;
211 |
--------------------------------------------------------------------------------
/utils/doclint/check_public_api/MDBuilder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const Documentation = require('./Documentation');
18 | const commonmark = require('commonmark');
19 |
20 | class MDOutline {
21 | /**
22 | * @param {!Page} page
23 | * @param {string} text
24 | * @return {!MDOutline}
25 | */
26 | static async create(page, text) {
27 | // Render markdown as HTML.
28 | const reader = new commonmark.Parser();
29 | const parsed = reader.parse(text);
30 | const writer = new commonmark.HtmlRenderer();
31 | const html = writer.render(parsed);
32 |
33 | // Extract headings.
34 | await page.setContent(html);
35 | const {classes, errors} = await page.evaluate(() => {
36 | const classes = [];
37 | let currentClass = {};
38 | let member = {};
39 | const errors = [];
40 | for (const element of document.body.querySelectorAll('h3, h4, h4 + ul > li')) {
41 | if (element.matches('h3')) {
42 | currentClass = {
43 | name: element.textContent,
44 | members: [],
45 | };
46 | classes.push(currentClass);
47 | } else if (element.matches('h4')) {
48 | member = {
49 | name: element.textContent,
50 | args: [],
51 | hasReturn: false
52 | };
53 | currentClass.members.push(member);
54 | } else if (element.matches('li') && element.firstChild.matches && element.firstChild.matches('code')) {
55 | member.args.push(element.firstChild.textContent);
56 | } else if (element.matches('li') && element.firstChild.nodeType === Element.TEXT_NODE && element.firstChild.textContent.toLowerCase().startsWith('retur')) {
57 | member.hasReturn = true;
58 | const expectedText = 'returns: ';
59 | let actualText = element.firstChild.textContent;
60 | let angleIndex = actualText.indexOf('<');
61 | let spaceIndex = actualText.indexOf(' ');
62 | angleIndex = angleIndex === -1 ? actualText.length : angleIndex;
63 | spaceIndex = spaceIndex === -1 ? actualText.length : spaceIndex + 1;
64 | actualText = actualText.substring(0, Math.min(angleIndex, spaceIndex));
65 | if (actualText !== expectedText)
66 | errors.push(`${member.name} has mistyped 'return' type declaration: expected exactly '${expectedText}', found '${actualText}'.`);
67 | }
68 | }
69 | return {classes, errors};
70 | });
71 | return new MDOutline(classes, errors);
72 | }
73 |
74 | constructor(classes, errors) {
75 | this.classes = [];
76 | this.errors = errors;
77 | const classHeading = /^class: (\w+)$/;
78 | const constructorRegex = /^new (\w+)\((.*)\)$/;
79 | const methodRegex = /^(\w+)\.([\w$]+)\((.*)\)$/;
80 | const propertyRegex = /^(\w+)\.(\w+)$/;
81 | const eventRegex = /^event: '(\w+)'$/;
82 | let currentClassName = null;
83 | let currentClassMembers = [];
84 | for (const cls of classes) {
85 | const match = cls.name.match(classHeading);
86 | if (!match)
87 | continue;
88 | currentClassName = match[1];
89 | for (const member of cls.members) {
90 | if (constructorRegex.test(member.name)) {
91 | const match = member.name.match(constructorRegex);
92 | handleMethod.call(this, member, match[1], 'constructor', match[2]);
93 | } else if (methodRegex.test(member.name)) {
94 | const match = member.name.match(methodRegex);
95 | handleMethod.call(this, member, match[1], match[2], match[3]);
96 | } else if (propertyRegex.test(member.name)) {
97 | const match = member.name.match(propertyRegex);
98 | handleProperty.call(this, member, match[1], match[2]);
99 | } else if (eventRegex.test(member.name)) {
100 | const match = member.name.match(eventRegex);
101 | handleEvent.call(this, member, match[1]);
102 | }
103 | }
104 | flushClassIfNeeded.call(this);
105 | }
106 |
107 | function handleMethod(member, className, methodName, parameters) {
108 | if (!currentClassName || !className || !methodName || className.toLowerCase() !== currentClassName.toLowerCase()) {
109 | this.errors.push(`Failed to process header as method: ${member.name}`);
110 | return;
111 | }
112 | parameters = parameters.trim().replace(/[\[\]]/g, '');
113 | if (parameters !== member.args.join(', '))
114 | this.errors.push(`Heading arguments for "${member.name}" do not match described ones, i.e. "${parameters}" != "${member.args.join(', ')}"`);
115 | const args = member.args.map(arg => new Documentation.Argument(arg));
116 | const method = Documentation.Member.createMethod(methodName, args, member.hasReturn, false);
117 | currentClassMembers.push(method);
118 | }
119 |
120 | function handleProperty(member, className, propertyName) {
121 | if (!currentClassName || !className || !propertyName || className.toLowerCase() !== currentClassName.toLowerCase()) {
122 | this.errors.push(`Failed to process header as property: ${member.name}`);
123 | return;
124 | }
125 | currentClassMembers.push(Documentation.Member.createProperty(propertyName));
126 | }
127 |
128 | function handleEvent(member, eventName) {
129 | if (!currentClassName || !eventName) {
130 | this.errors.push(`Failed to process header as event: ${member.name}`);
131 | return;
132 | }
133 | currentClassMembers.push(Documentation.Member.createEvent(eventName));
134 | }
135 |
136 | function flushClassIfNeeded() {
137 | if (currentClassName === null)
138 | return;
139 | this.classes.push(new Documentation.Class(currentClassName, currentClassMembers));
140 | currentClassName = null;
141 | currentClassMembers = [];
142 | }
143 | }
144 | }
145 |
146 | /**
147 | * @param {!Page} page
148 | * @param {!Array} sources
149 | * @return {!Promise<{documentation: !Documentation, errors: !Array}>}
150 | */
151 | module.exports = async function(page, sources) {
152 | const classes = [];
153 | const errors = [];
154 | for (const source of sources) {
155 | const outline = await MDOutline.create(page, source.text());
156 | classes.push(...outline.classes);
157 | errors.push(...outline.errors);
158 | }
159 | const documentation = new Documentation(classes);
160 | return { documentation, errors };
161 | };
162 |
163 |
--------------------------------------------------------------------------------
/lib/ElementHandle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Google Inc. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | const path = require('path');
17 | const {JSHandle} = require('./ExecutionContext');
18 | const {helper, debugError} = require('./helper');
19 |
20 | class ElementHandle extends JSHandle {
21 | /**
22 | * @param {!Puppeteer.ExecutionContext} context
23 | * @param {!Puppeteer.CDPSession} client
24 | * @param {!Object} remoteObject
25 | * @param {!Puppeteer.Page} page
26 | */
27 | constructor(context, client, remoteObject, page) {
28 | super(context, client, remoteObject);
29 | this._client = client;
30 | this._remoteObject = remoteObject;
31 | this._page = page;
32 | this._disposed = false;
33 | }
34 |
35 | /**
36 | * @override
37 | * @return {?ElementHandle}
38 | */
39 | asElement() {
40 | return this;
41 | }
42 |
43 | async _scrollIntoViewIfNeeded() {
44 | const error = await this.executionContext().evaluate(element => {
45 | if (!element.isConnected)
46 | return 'Node is detached from document';
47 | if (element.nodeType !== Node.ELEMENT_NODE)
48 | return 'Node is not of type HTMLElement';
49 | element.scrollIntoViewIfNeeded();
50 | return false;
51 | }, this);
52 | if (error)
53 | throw new Error(error);
54 | }
55 |
56 | /**
57 | * @return {!Promise<{x: number, y: number}>}
58 | */
59 | async _visibleCenter() {
60 | await this._scrollIntoViewIfNeeded();
61 | const box = await this.boundingBox();
62 | if (!box)
63 | throw new Error('Node is not visible');
64 | return {
65 | x: box.x + box.width / 2,
66 | y: box.y + box.height / 2
67 | };
68 | }
69 |
70 | async hover() {
71 | const {x, y} = await this._visibleCenter();
72 | await this._page.mouse.move(x, y);
73 | }
74 |
75 | /**
76 | * @param {!Object=} options
77 | */
78 | async click(options = {}) {
79 | const {x, y} = await this._visibleCenter();
80 | await this._page.mouse.click(x, y, options);
81 | }
82 |
83 | /**
84 | * @param {!Array} filePaths
85 | * @return {!Promise}
86 | */
87 | async uploadFile(...filePaths) {
88 | const files = filePaths.map(filePath => path.resolve(filePath));
89 | const objectId = this._remoteObject.objectId;
90 | return this._client.send('DOM.setFileInputFiles', { objectId, files });
91 | }
92 |
93 | async tap() {
94 | const {x, y} = await this._visibleCenter();
95 | await this._page.touchscreen.tap(x, y);
96 | }
97 |
98 | async focus() {
99 | await this.executionContext().evaluate(element => element.focus(), this);
100 | }
101 |
102 | /**
103 | * @param {string} text
104 | * @param {{delay: (number|undefined)}=} options
105 | */
106 | async type(text, options) {
107 | await this.focus();
108 | await this._page.keyboard.type(text, options);
109 | }
110 |
111 | /**
112 | * @param {string} key
113 | * @param {!Object=} options
114 | */
115 | async press(key, options) {
116 | await this.focus();
117 | await this._page.keyboard.press(key, options);
118 | }
119 |
120 | /**
121 | * @return {!Promise{x: number, y: number, width: number, height: number}>}
122 | */
123 | async boundingBox() {
124 | const result = await this._client.send('DOM.getBoxModel', {
125 | objectId: this._remoteObject.objectId
126 | }).catch(error => void debugError(error));
127 |
128 | if (!result)
129 | return null;
130 |
131 | const quad = result.model.border;
132 | const x = Math.min(quad[0], quad[2], quad[4], quad[6]);
133 | const y = Math.min(quad[1], quad[3], quad[5], quad[7]);
134 | const width = Math.max(quad[0], quad[2], quad[4], quad[6]) - x;
135 | const height = Math.max(quad[1], quad[3], quad[5], quad[7]) - y;
136 |
137 | return {x, y, width, height};
138 | }
139 |
140 | /**
141 | *
142 | * @param {!Object=} options
143 | * @returns {!Promise