From 17cd8703f973267c08238cd6bbe93b37f7b36622 Mon Sep 17 00:00:00 2001 From: Jack Franklin Date: Thu, 9 Apr 2020 06:56:25 +0100 Subject: [PATCH] chore: migrate unit tests to Mocha (#5600) Rather than maintain our own test runner we should instead lean on the community and use Mocha which is very popular and also our test runner of choice in DevTools too. Note that this commit doesn't remove the TestRunner source as it's still used for other unit tests, but they will be updated in a future PR and then we can remove the TestRunner. The main bulk of this PR is updating the tests as the old TestRunner passed in contextual data via the `it` function callback whereas Mocha does not, so we introduce some helpers for the tests to make it easier. --- .eslintrc.js | 9 +- .mocharc.js | 6 + package.json | 8 +- test/CDPSession.spec.js | 129 +++--- test/README.md | 34 ++ test/accessibility.spec.js | 625 +++++++++++++++-------------- test/browser.spec.js | 39 +- test/browsercontext.spec.js | 281 +++++++------ test/chromiumonly.spec.js | 188 ++++----- test/click.spec.js | 478 ++++++++++++---------- test/cookies.spec.js | 89 ++-- test/coverage-utils.js | 84 ++++ test/coverage.spec.js | 107 +++-- test/defaultbrowsercontext.spec.js | 139 ++++--- test/dialog.spec.js | 62 +-- test/elementhandle.spec.js | 86 ++-- test/emulation.spec.js | 94 +++-- test/evaluation.spec.js | 202 +++++++--- test/fixtures.spec.js | 103 ++--- test/frame.spec.js | 67 +++- test/headful.spec.js | 82 ++-- test/ignorehttpserrors.spec.js | 172 ++++---- test/input.spec.js | 94 +++-- test/jshandle.spec.js | 94 +++-- test/keyboard.spec.js | 471 ++++++++++++---------- test/launcher.spec.js | 122 ++++-- test/mocha-utils.js | 155 +++++++ test/mouse.spec.js | 248 ++++++------ test/navigation.spec.js | 224 ++++++++--- test/network.spec.js | 183 ++++++--- test/oopif.spec.js | 82 ++-- test/page.spec.js | 526 +++++++++++++++++------- test/puppeteer.spec.js | 173 -------- test/queryselector.spec.js | 120 ++++-- test/requestinterception.spec.js | 173 +++++--- test/screenshot.spec.js | 89 ++-- test/target.spec.js | 297 +++++++------- test/test.js | 121 ------ test/touchscreen.spec.js | 44 +- test/tracing.spec.js | 161 ++++---- test/utils.js | 52 --- test/waittask.spec.js | 220 +++++++--- test/worker.spec.js | 142 ++++--- 43 files changed, 4111 insertions(+), 2764 deletions(-) create mode 100644 .mocharc.js create mode 100644 test/README.md create mode 100644 test/coverage-utils.js create mode 100644 test/mocha-utils.js delete mode 100644 test/puppeteer.spec.js delete mode 100644 test/test.js diff --git a/.eslintrc.js b/.eslintrc.js index a6939035c9be3..49c970c4b6b7f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,6 +10,10 @@ module.exports = { "ecmaVersion": 9 }, + "plugins": [ + "mocha" + ], + /** * ESLint rules * @@ -107,6 +111,9 @@ module.exports = { "indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }], "key-spacing": [2, { "beforeColon": false - }] + }], + + // ensure we don't have any it.only or describe.only in prod + "mocha/no-exclusive-tests": "error" } }; diff --git a/.mocharc.js b/.mocharc.js new file mode 100644 index 0000000000000..32bbf7ee51d03 --- /dev/null +++ b/.mocharc.js @@ -0,0 +1,6 @@ +module.exports = { + file: ['./test/mocha-utils.js'], + spec: 'test/*.spec.js', + reporter: 'dot', + timeout: process.env.PUPPETEER_PRODUCT === 'firefox' ? 15 * 1000 : 10 * 1000, +}; diff --git a/package.json b/package.json index e165913208b79..2035cd263540e 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "firefox_revision": "latest" }, "scripts": { - "unit": "node test/test.js", - "funit": "PUPPETEER_PRODUCT=firefox node test/test.js", + "unit": "mocha --config .mocharc.js", + "coverage": "cross-env COVERAGE=1 npm run unit", + "funit": "PUPPETEER_PRODUCT=firefox npm run unit", "debug-unit": "node --inspect-brk test/test.js", "test-doclint": "node utils/doclint/check_public_api/test/test.js && node utils/doclint/preprocessor/test.js", "test": "npm run tsc && npm run lint --silent && npm run coverage && npm run test-doclint && npm run test-types && node utils/testrunner/test/test.js", @@ -22,7 +23,6 @@ "install": "node install.js", "lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc", "doc": "node utils/doclint/cli.js", - "coverage": "cross-env COVERAGE=true npm run unit", "tsc": "tsc --version && tsc -p . && cp src/protocol.d.ts lib/ && cp src/externs.d.ts lib/", "apply-next-version": "node utils/apply_next_version.js", "bundle": "npm run tsc && npx browserify -r ./index.js:puppeteer -o utils/browser/puppeteer-web.js", @@ -57,10 +57,12 @@ "commonmark": "^0.28.1", "cross-env": "^5.0.5", "eslint": "^6.8.0", + "eslint-plugin-mocha": "^6.3.0", "esprima": "^4.0.0", "expect": "^25.2.7", "jpeg-js": "^0.3.4", "minimist": "^1.2.0", + "mocha": "^7.1.1", "ncp": "^2.0.0", "pixelmatch": "^4.0.2", "pngjs": "^3.3.3", diff --git a/test/CDPSession.spec.js b/test/CDPSession.spec.js index 2647d2a2e3698..5f16d0566ab74 100644 --- a/test/CDPSession.spec.js +++ b/test/CDPSession.spec.js @@ -15,69 +15,78 @@ */ const {waitEvent} = require('./utils'); +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describeChromeOnly('Target.createCDPSession', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); - describe('Target.createCDPSession', function() { - it('should work', async function({page, server}) { - const client = await page.target().createCDPSession(); + it('should work', async() => { + const { page } = getTestState(); - await Promise.all([ - client.send('Runtime.enable'), - client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' }) - ]); - const foo = await page.evaluate(() => window.foo); - expect(foo).toBe('bar'); - }); - it('should send events', async function({page, server}) { - const client = await page.target().createCDPSession(); - await client.send('Network.enable'); - const events = []; - client.on('Network.requestWillBeSent', event => events.push(event)); - await page.goto(server.EMPTY_PAGE); - expect(events.length).toBe(1); - }); - it('should enable and disable domains independently', async function({page, server}) { - const client = await page.target().createCDPSession(); - await client.send('Runtime.enable'); - await client.send('Debugger.enable'); - // JS coverage enables and then disables Debugger domain. - await page.coverage.startJSCoverage(); - await page.coverage.stopJSCoverage(); - // generate a script in page and wait for the event. - const [event] = await Promise.all([ - waitEvent(client, 'Debugger.scriptParsed'), - page.evaluate('//# sourceURL=foo.js') - ]); + const client = await page.target().createCDPSession(); + + await Promise.all([ + client.send('Runtime.enable'), + client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' }) + ]); + const foo = await page.evaluate(() => window.foo); + expect(foo).toBe('bar'); + }); + it('should send events', async() => { + const { page, server } = getTestState(); + + const client = await page.target().createCDPSession(); + await client.send('Network.enable'); + const events = []; + client.on('Network.requestWillBeSent', event => events.push(event)); + await page.goto(server.EMPTY_PAGE); + expect(events.length).toBe(1); + }); + it('should enable and disable domains independently', async() => { + const { page } = getTestState(); + + const client = await page.target().createCDPSession(); + await client.send('Runtime.enable'); + await client.send('Debugger.enable'); + // JS coverage enables and then disables Debugger domain. + await page.coverage.startJSCoverage(); + await page.coverage.stopJSCoverage(); + // generate a script in page and wait for the event. + const [event] = await Promise.all([ + waitEvent(client, 'Debugger.scriptParsed'), + page.evaluate('//# sourceURL=foo.js') + ]); // expect events to be dispatched. - expect(event.url).toBe('foo.js'); - }); - it('should be able to detach session', async function({page, server}) { - const client = await page.target().createCDPSession(); - await client.send('Runtime.enable'); - const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true}); - expect(evalResponse.result.value).toBe(3); - await client.detach(); - let error = null; - try { - await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true}); - } catch (e) { - error = e; - } - expect(error.message).toContain('Session closed.'); - }); - it('should throw nice errors', async function({page}) { - const client = await page.target().createCDPSession(); - const error = await theSourceOfTheProblems().catch(error => error); - expect(error.stack).toContain('theSourceOfTheProblems'); - expect(error.message).toContain('ThisCommand.DoesNotExist'); + expect(event.url).toBe('foo.js'); + }); + it('should be able to detach session', async() => { + const { page } = getTestState(); + + const client = await page.target().createCDPSession(); + await client.send('Runtime.enable'); + const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true}); + expect(evalResponse.result.value).toBe(3); + await client.detach(); + let error = null; + try { + await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true}); + } catch (e) { + error = e; + } + expect(error.message).toContain('Session closed.'); + }); + it('should throw nice errors', async() => { + const { page } = getTestState(); + + const client = await page.target().createCDPSession(); + const error = await theSourceOfTheProblems().catch(error => error); + expect(error.stack).toContain('theSourceOfTheProblems'); + expect(error.message).toContain('ThisCommand.DoesNotExist'); - async function theSourceOfTheProblems() { - await client.send('ThisCommand.DoesNotExist'); - } - }); + async function theSourceOfTheProblems() { + await client.send('ThisCommand.DoesNotExist'); + } }); -}; +}); diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000000000..f69d4fe02abf2 --- /dev/null +++ b/test/README.md @@ -0,0 +1,34 @@ +# Puppeteer unit tests + +Unit tests in Puppeteer are written using [Mocha] as the test runner and [Expect] as the assertions library. + +## Test state + + +We have some common setup that runs before each test and is defined in `mocha-utils.js`. + +You can use the `getTestState` function to read state. It exposes the following that you can use in your tests. These will be reset/tidied between tests automatically for you: + +* `puppeteer`: an instance of the Puppeteer library. This is exactly what you'd get if you ran `require('puppeteer')`. +* `puppeteerPath`: the path to the root source file for Puppeteer. +* `defaultBrowserOptions`: the default options the Puppeteer browser is launched from in test mode, so tests can use them and override if required. +* `server`: a dummy test server instance (see `utils/testserver` for more). +* `httpsServer`: a dummy test server HTTPS instance (see `utils/testserver` for more). +* `isFirefox`: true if running in Firefox. +* `isChrome`: true if running Chromium. +* `isHeadless`: true if the test is in headless mode. + +If your test needs a browser instance, you can use the `setupTestBrowserHooks()` function which will automatically configure a browser that will be cleaned between each test suite run. You access this via `getTestState()`. + +If your test needs a Puppeteer page and context, you can use the `setupTestPageAndContextHooks()` function which will configure these. You can access `page` and `context` from `getTestState()` once you have done this. + +The best place to look is an existing test to see how they use the helpers. + +## Skipping tests for Firefox + +Tests that are not expected to pass in Firefox can be skipped. You can skip an individual test by using `itFailsFirefox` rather than `it`. Similarly you can skip a describe block with `describeFailsFirefox`. + +There is also `describeChromeOnly` which will only execute the test if running in Chromium. Note that this is different from `describeFailsFirefox`: the goal is to get any `FailsFirefox` calls passing in Firefox, whereas `describeChromeOnly` should be used to test behaviour that will only ever apply in Chromium. + +[Mocha]: https://mochajs.org/ +[Expect]: https://www.npmjs.com/package/expect diff --git a/test/accessibility.spec.js b/test/accessibility.spec.js index 754afcd22297a..68022c7807cdd 100644 --- a/test/accessibility.spec.js +++ b/test/accessibility.spec.js @@ -14,14 +14,17 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, FFOX}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); - describe_fails_ffox('Accessibility', function() { - it('should work', async function({page}) { - await page.setContent(` +describeFailsFirefox('Accessibility', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + it('should work', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(` Accessibility Test @@ -42,296 +45,333 @@ module.exports.addTests = function({testRunner, expect, FFOX}) { `); - await page.focus('[placeholder="Empty input"]'); - const golden = FFOX ? { + await page.focus('[placeholder="Empty input"]'); + const golden = isFirefox ? { + role: 'document', + name: 'Accessibility Test', + children: [ + {role: 'text leaf', name: 'Hello World'}, + {role: 'heading', name: 'Inputs', level: 1}, + {role: 'entry', name: 'Empty input', focused: true}, + {role: 'entry', name: 'readonly input', readonly: true}, + {role: 'entry', name: 'disabled input', disabled: true}, + {role: 'entry', name: 'Input with whitespace', value: ' '}, + {role: 'entry', name: '', value: 'value only'}, + {role: 'entry', name: '', value: 'and a value'}, // firefox doesn't use aria-placeholder for the name + {role: 'entry', name: '', value: 'and a value', description: 'This is a description!'}, // and here + {role: 'combobox', name: '', value: 'First Option', haspopup: true, children: [ + {role: 'combobox option', name: 'First Option', selected: true}, + {role: 'combobox option', name: 'Second Option'}] + }] + } : { + role: 'WebArea', + name: 'Accessibility Test', + children: [ + {role: 'text', name: 'Hello World'}, + {role: 'heading', name: 'Inputs', level: 1}, + {role: 'textbox', name: 'Empty input', focused: true}, + {role: 'textbox', name: 'readonly input', readonly: true}, + {role: 'textbox', name: 'disabled input', disabled: true}, + {role: 'textbox', name: 'Input with whitespace', value: ' '}, + {role: 'textbox', name: '', value: 'value only'}, + {role: 'textbox', name: 'placeholder', value: 'and a value'}, + {role: 'textbox', name: 'placeholder', value: 'and a value', description: 'This is a description!'}, + {role: 'combobox', name: '', value: 'First Option', children: [ + {role: 'menuitem', name: 'First Option', selected: true}, + {role: 'menuitem', name: 'Second Option'}] + }] + }; + expect(await page.accessibility.snapshot()).toEqual(golden); + }); + it('should report uninteresting nodes', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(``); + await page.focus('textarea'); + const golden = isFirefox ? { + role: 'entry', + name: '', + value: 'hi', + focused: true, + multiline: true, + children: [{ + role: 'text leaf', + name: 'hi' + }] + } : { + role: 'textbox', + name: '', + value: 'hi', + focused: true, + multiline: true, + children: [{ + role: 'generic', + name: '', + children: [{ + role: 'text', name: 'hi' + }] + }] + }; + expect(findFocusedNode(await page.accessibility.snapshot({interestingOnly: false}))).toEqual(golden); + }); + it('roledescription', async() => { + const { page } = getTestState(); + + await page.setContent('
Hi
'); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0].roledescription).toEqual('foo'); + }); + it('orientation', async() => { + const { page } = getTestState(); + + await page.setContent('11'); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0].orientation).toEqual('vertical'); + }); + it('autocomplete', async() => { + const { page } = getTestState(); + + await page.setContent(''); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0].autocomplete).toEqual('list'); + }); + it('multiselectable', async() => { + const { page } = getTestState(); + + await page.setContent('
hey
'); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0].multiselectable).toEqual(true); + }); + it('keyshortcuts', async() => { + const { page } = getTestState(); + + await page.setContent('
hey
'); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0].keyshortcuts).toEqual('foo'); + }); + describe('filtering children of leaf nodes', function() { + it('should not report text nodes inside controls', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(` +
+
Tab1
+
Tab2
+
`); + const golden = isFirefox ? { role: 'document', - name: 'Accessibility Test', - children: [ - {role: 'text leaf', name: 'Hello World'}, - {role: 'heading', name: 'Inputs', level: 1}, - {role: 'entry', name: 'Empty input', focused: true}, - {role: 'entry', name: 'readonly input', readonly: true}, - {role: 'entry', name: 'disabled input', disabled: true}, - {role: 'entry', name: 'Input with whitespace', value: ' '}, - {role: 'entry', name: '', value: 'value only'}, - {role: 'entry', name: '', value: 'and a value'}, // firefox doesn't use aria-placeholder for the name - {role: 'entry', name: '', value: 'and a value', description: 'This is a description!'}, // and here - {role: 'combobox', name: '', value: 'First Option', haspopup: true, children: [ - {role: 'combobox option', name: 'First Option', selected: true}, - {role: 'combobox option', name: 'Second Option'}] - }] + name: '', + children: [{ + role: 'pagetab', + name: 'Tab1', + selected: true + }, { + role: 'pagetab', + name: 'Tab2' + }] } : { role: 'WebArea', - name: 'Accessibility Test', - children: [ - {role: 'text', name: 'Hello World'}, - {role: 'heading', name: 'Inputs', level: 1}, - {role: 'textbox', name: 'Empty input', focused: true}, - {role: 'textbox', name: 'readonly input', readonly: true}, - {role: 'textbox', name: 'disabled input', disabled: true}, - {role: 'textbox', name: 'Input with whitespace', value: ' '}, - {role: 'textbox', name: '', value: 'value only'}, - {role: 'textbox', name: 'placeholder', value: 'and a value'}, - {role: 'textbox', name: 'placeholder', value: 'and a value', description: 'This is a description!'}, - {role: 'combobox', name: '', value: 'First Option', children: [ - {role: 'menuitem', name: 'First Option', selected: true}, - {role: 'menuitem', name: 'Second Option'}] - }] + name: '', + children: [{ + role: 'tab', + name: 'Tab1', + selected: true + }, { + role: 'tab', + name: 'Tab2' + }] }; expect(await page.accessibility.snapshot()).toEqual(golden); }); - it('should report uninteresting nodes', async function({page}) { - await page.setContent(``); - await page.focus('textarea'); - const golden = FFOX ? { - role: 'entry', + it('rich text editable fields should have children', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(` +
+ Edit this image: my fake image +
`); + const golden = isFirefox ? { + role: 'section', name: '', - value: 'hi', - focused: true, - multiline: true, children: [{ role: 'text leaf', - name: 'hi' + name: 'Edit this image: ' + }, { + role: 'text', + name: 'my fake image' }] } : { - role: 'textbox', + role: 'generic', name: '', - value: 'hi', - focused: true, - multiline: true, + value: 'Edit this image: ', children: [{ - role: 'generic', - name: '', - children: [{ - role: 'text', name: 'hi' - }] + role: 'text', + name: 'Edit this image:' + }, { + role: 'img', + name: 'my fake image' }] }; - expect(findFocusedNode(await page.accessibility.snapshot({interestingOnly: false}))).toEqual(golden); - }); - it('roledescription', async({page}) => { - await page.setContent('
Hi
'); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].roledescription).toEqual('foo'); - }); - it('orientation', async({page}) => { - await page.setContent('11'); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].orientation).toEqual('vertical'); - }); - it('autocomplete', async({page}) => { - await page.setContent(''); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].autocomplete).toEqual('list'); + expect(snapshot.children[0]).toEqual(golden); }); - it('multiselectable', async({page}) => { - await page.setContent('
hey
'); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].multiselectable).toEqual(true); - }); - it('keyshortcuts', async({page}) => { - await page.setContent('
hey
'); + it('rich text editable fields with role should have children', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(` +
+ Edit this image: my fake image +
`); + const golden = isFirefox ? { + role: 'entry', + name: '', + value: 'Edit this image: my fake image', + children: [{ + role: 'text', + name: 'my fake image' + }] + } : { + role: 'textbox', + name: '', + value: 'Edit this image: ', + children: [{ + role: 'text', + name: 'Edit this image:' + }, { + role: 'img', + name: 'my fake image' + }] + }; const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0].keyshortcuts).toEqual('foo'); + expect(snapshot.children[0]).toEqual(golden); }); - describe('filtering children of leaf nodes', function() { - it('should not report text nodes inside controls', async function({page}) { - await page.setContent(` -
-
Tab1
-
Tab2
-
`); - const golden = FFOX ? { - role: 'document', - name: '', - children: [{ - role: 'pagetab', - name: 'Tab1', - selected: true - }, { - role: 'pagetab', - name: 'Tab2' - }] - } : { - role: 'WebArea', - name: '', - children: [{ - role: 'tab', - name: 'Tab1', - selected: true - }, { - role: 'tab', - name: 'Tab2' - }] - }; - expect(await page.accessibility.snapshot()).toEqual(golden); - }); - it('rich text editable fields should have children', async function({page}) { + + // Firefox does not support contenteditable="plaintext-only". + describeFailsFirefox('plaintext contenteditable', function() { + it('plain text field with role should not have children', async() => { + const { page } = getTestState(); + await page.setContent(` -
- Edit this image: my fake image -
`); - const golden = FFOX ? { - role: 'section', - name: '', - children: [{ - role: 'text leaf', - name: 'Edit this image: ' - }, { - role: 'text', - name: 'my fake image' - }] - } : { - role: 'generic', - name: '', - value: 'Edit this image: ', - children: [{ - role: 'text', - name: 'Edit this image:' - }, { - role: 'img', - name: 'my fake image' - }] - }; +
Edit this image:my fake image
`); const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); - }); - it('rich text editable fields with role should have children', async function({page}) { - await page.setContent(` -
- Edit this image: my fake image -
`); - const golden = FFOX ? { - role: 'entry', - name: '', - value: 'Edit this image: my fake image', - children: [{ - role: 'text', - name: 'my fake image' - }] - } : { + expect(snapshot.children[0]).toEqual({ role: 'textbox', name: '', - value: 'Edit this image: ', - children: [{ - role: 'text', - name: 'Edit this image:' - }, { - role: 'img', - name: 'my fake image' - }] - }; - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); - }); - // Firefox does not support contenteditable="plaintext-only". - !FFOX && describe('plaintext contenteditable', function() { - it('plain text field with role should not have children', async function({page}) { - await page.setContent(` -
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'textbox', - name: '', - value: 'Edit this image:' - }); + value: 'Edit this image:' }); - it('plain text field without role should not have content', async function({page}) { - await page.setContent(` + }); + it('plain text field without role should not have content', async() => { + const { page } = getTestState(); + + await page.setContent(`
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'generic', - name: '' - }); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual({ + role: 'generic', + name: '' }); - it('plain text field with tabindex and without role should not have content', async function({page}) { - await page.setContent(` + }); + it('plain text field with tabindex and without role should not have content', async() => { + const { page } = getTestState(); + + await page.setContent(`
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'generic', - name: '' - }); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual({ + role: 'generic', + name: '' }); }); - it('non editable textbox with role and tabIndex and label should not have children', async function({page}) { - await page.setContent(` + }); + it('non editable textbox with role and tabIndex and label should not have children', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(`
this is the inner content yo
`); - const golden = FFOX ? { - role: 'entry', - name: 'my favorite textbox', - value: 'this is the inner content yo' - } : { - role: 'textbox', - name: 'my favorite textbox', - value: 'this is the inner content ' - }; - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); - }); - it('checkbox with and tabIndex and label should not have children', async function({page}) { - await page.setContent(` + const golden = isFirefox ? { + role: 'entry', + name: 'my favorite textbox', + value: 'this is the inner content yo' + } : { + role: 'textbox', + name: 'my favorite textbox', + value: 'this is the inner content ' + }; + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual(golden); + }); + it('checkbox with and tabIndex and label should not have children', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(`
this is the inner content yo
`); - const golden = FFOX ? { - role: 'checkbutton', - name: 'my favorite checkbox', - checked: true - } : { - role: 'checkbox', - name: 'my favorite checkbox', - checked: true - }; - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); - }); - it('checkbox without label should not have children', async function({page}) { - await page.setContent(` + const golden = isFirefox ? { + role: 'checkbutton', + name: 'my favorite checkbox', + checked: true + } : { + role: 'checkbox', + name: 'my favorite checkbox', + checked: true + }; + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual(golden); + }); + it('checkbox without label should not have children', async() => { + const { page, isFirefox } = getTestState(); + + await page.setContent(`
this is the inner content yo
`); - const golden = FFOX ? { - role: 'checkbutton', - name: 'this is the inner content yo', - checked: true - } : { - role: 'checkbox', - name: 'this is the inner content yo', - checked: true - }; - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual(golden); - }); + const golden = isFirefox ? { + role: 'checkbutton', + name: 'this is the inner content yo', + checked: true + } : { + role: 'checkbox', + name: 'this is the inner content yo', + checked: true + }; + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual(golden); + }); + + describe('root option', function() { + it('should work a button', async() => { + const { page } = getTestState(); - describe('root option', function() { - it('should work a button', async({page}) => { - await page.setContent(``); + await page.setContent(``); - const button = await page.$('button'); - expect(await page.accessibility.snapshot({root: button})).toEqual({ - role: 'button', - name: 'My Button' - }); + const button = await page.$('button'); + expect(await page.accessibility.snapshot({root: button})).toEqual({ + role: 'button', + name: 'My Button' }); - it('should work an input', async({page}) => { - await page.setContent(``); + }); + it('should work an input', async() => { + const { page } = getTestState(); + + await page.setContent(``); - const input = await page.$('input'); - expect(await page.accessibility.snapshot({root: input})).toEqual({ - role: 'textbox', - name: 'My Input', - value: 'My Value' - }); + const input = await page.$('input'); + expect(await page.accessibility.snapshot({root: input})).toEqual({ + role: 'textbox', + name: 'My Input', + value: 'My Value' }); - it('should work a menu', async({page}) => { - await page.setContent(` + }); + it('should work a menu', async() => { + const { page } = getTestState(); + + await page.setContent(`
First Item
Second Item
@@ -339,51 +379,54 @@ module.exports.addTests = function({testRunner, expect, FFOX}) {
`); - const menu = await page.$('div[role="menu"]'); - expect(await page.accessibility.snapshot({root: menu})).toEqual({ - role: 'menu', - name: 'My Menu', - children: + const menu = await page.$('div[role="menu"]'); + expect(await page.accessibility.snapshot({root: menu})).toEqual({ + role: 'menu', + name: 'My Menu', + children: [ { role: 'menuitem', name: 'First Item' }, { role: 'menuitem', name: 'Second Item' }, { role: 'menuitem', name: 'Third Item' } ] - }); }); - it('should return null when the element is no longer in DOM', async({page}) => { - await page.setContent(``); - const button = await page.$('button'); - await page.$eval('button', button => button.remove()); - expect(await page.accessibility.snapshot({root: button})).toEqual(null); - }); - it('should support the interestingOnly option', async({page}) => { - await page.setContent(`
`); - const div = await page.$('div'); - expect(await page.accessibility.snapshot({root: div})).toEqual(null); - expect(await page.accessibility.snapshot({root: div, interestingOnly: false})).toEqual({ - role: 'generic', - name: '', - children: [ - { - role: 'button', - name: 'My Button', - children: [ - { role: 'text', name: 'My Button' }, - ], - }, - ], - }); + }); + it('should return null when the element is no longer in DOM', async() => { + const { page } = getTestState(); + + await page.setContent(``); + const button = await page.$('button'); + await page.$eval('button', button => button.remove()); + expect(await page.accessibility.snapshot({root: button})).toEqual(null); + }); + it('should support the interestingOnly option', async() => { + const { page } = getTestState(); + + await page.setContent(`
`); + const div = await page.$('div'); + expect(await page.accessibility.snapshot({root: div})).toEqual(null); + expect(await page.accessibility.snapshot({root: div, interestingOnly: false})).toEqual({ + role: 'generic', + name: '', + children: [ + { + role: 'button', + name: 'My Button', + children: [ + { role: 'text', name: 'My Button' }, + ], + }, + ], }); }); }); - function findFocusedNode(node) { - if (node.focused) - return node; - for (const child of node.children || []) { - const focusedChild = findFocusedNode(child); - if (focusedChild) - return focusedChild; - } - return null; - } }); -}; + function findFocusedNode(node) { + if (node.focused) + return node; + for (const child of node.children || []) { + const focusedChild = findFocusedNode(child); + if (focusedChild) + return focusedChild; + } + return null; + } +}); diff --git a/test/browser.spec.js b/test/browser.spec.js index 61de5889afc7a..38f4188bb45a4 100644 --- a/test/browser.spec.js +++ b/test/browser.spec.js @@ -14,24 +14,29 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHROME}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks} = require('./mocha-utils'); + +describe('Browser specs', function() { + setupTestBrowserHooks(); describe('Browser.version', function() { - it('should return whether we are in headless', async({browser}) => { + it('should return whether we are in headless', async() => { + const { browser, isHeadless } = getTestState(); + const version = await browser.version(); expect(version.length).toBeGreaterThan(0); - expect(version.startsWith('Headless')).toBe(headless); + expect(version.startsWith('Headless')).toBe(isHeadless); }); }); describe('Browser.userAgent', function() { - it('should include WebKit', async({browser}) => { + it('should include WebKit', async() => { + const { browser, isChrome } = getTestState(); + const userAgent = await browser.userAgent(); expect(userAgent.length).toBeGreaterThan(0); - if (CHROME) + if (isChrome) expect(userAgent).toContain('WebKit'); else expect(userAgent).toContain('Gecko'); @@ -39,18 +44,24 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Browser.target', function() { - it('should return browser target', async({browser}) => { + it('should return browser target', async() => { + const { browser } = getTestState(); + const target = browser.target(); expect(target.type()).toBe('browser'); }); }); describe('Browser.process', function() { - it('should return child_process instance', async function({browser}) { + it('should return child_process instance', async() => { + const { browser } = getTestState(); + const process = await browser.process(); expect(process.pid).toBeGreaterThan(0); }); - it('should not return child_process for remote browser', async function({browser}) { + it('should not return child_process for remote browser', async() => { + const { browser, puppeteer } = getTestState(); + const browserWSEndpoint = browser.wsEndpoint(); const remoteBrowser = await puppeteer.connect({browserWSEndpoint}); expect(remoteBrowser.process()).toBe(null); @@ -59,7 +70,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Browser.isConnected', () => { - it('should set the browser connected state', async({browser}) => { + it('should set the browser connected state', async() => { + const { browser, puppeteer } = getTestState(); + const browserWSEndpoint = browser.wsEndpoint(); const newBrowser = await puppeteer.connect({browserWSEndpoint}); expect(newBrowser.isConnected()).toBe(true); @@ -67,4 +80,4 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(newBrowser.isConnected()).toBe(false); }); }); -}; +}); diff --git a/test/browsercontext.spec.js b/test/browsercontext.spec.js index 5fc0ff698c8e7..af243e377eea5 100644 --- a/test/browsercontext.spec.js +++ b/test/browsercontext.spec.js @@ -14,143 +14,160 @@ * limitations under the License. */ +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks} = require('./mocha-utils'); const utils = require('./utils'); -module.exports.addTests = function({testRunner, expect, puppeteer}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('BrowserContext', function() { - it_fails_ffox('should have default context', async function({browser, server}) { - expect(browser.browserContexts().length).toBe(1); - const defaultContext = browser.browserContexts()[0]; - expect(defaultContext.isIncognito()).toBe(false); - let error = null; - await defaultContext.close().catch(e => error = e); - expect(browser.defaultBrowserContext()).toBe(defaultContext); - expect(error.message).toContain('cannot be closed'); - }); - it_fails_ffox('should create new incognito context', async function({browser, server}) { - expect(browser.browserContexts().length).toBe(1); - const context = await browser.createIncognitoBrowserContext(); - expect(context.isIncognito()).toBe(true); - expect(browser.browserContexts().length).toBe(2); - expect(browser.browserContexts().indexOf(context) !== -1).toBe(true); - await context.close(); - expect(browser.browserContexts().length).toBe(1); - }); - it_fails_ffox('should close all belonging targets once closing context', async function({browser, server}) { - expect((await browser.pages()).length).toBe(1); +describe('BrowserContext', function() { + setupTestBrowserHooks(); + itFailsFirefox('should have default context', async() => { + const { browser } = getTestState(); + expect(browser.browserContexts().length).toEqual(1); + const defaultContext = browser.browserContexts()[0]; + expect(defaultContext.isIncognito()).toBe(false); + let error = null; + await defaultContext.close().catch(e => error = e); + expect(browser.defaultBrowserContext()).toBe(defaultContext); + expect(error.message).toContain('cannot be closed'); + }); + itFailsFirefox('should create new incognito context', async() => { + const { browser } = getTestState(); - const context = await browser.createIncognitoBrowserContext(); - await context.newPage(); - expect((await browser.pages()).length).toBe(2); - expect((await context.pages()).length).toBe(1); + expect(browser.browserContexts().length).toBe(1); + const context = await browser.createIncognitoBrowserContext(); + expect(context.isIncognito()).toBe(true); + expect(browser.browserContexts().length).toBe(2); + expect(browser.browserContexts().indexOf(context) !== -1).toBe(true); + await context.close(); + expect(browser.browserContexts().length).toBe(1); + }); + itFailsFirefox('should close all belonging targets once closing context', async() => { + const { browser } = getTestState(); - await context.close(); - expect((await browser.pages()).length).toBe(1); - }); - it_fails_ffox('window.open should use parent tab context', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - const page = await context.newPage(); - await page.goto(server.EMPTY_PAGE); - const [popupTarget] = await Promise.all([ - utils.waitEvent(browser, 'targetcreated'), - page.evaluate(url => window.open(url), server.EMPTY_PAGE) - ]); - expect(popupTarget.browserContext()).toBe(context); - await context.close(); - }); - it_fails_ffox('should fire target events', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - const events = []; - context.on('targetcreated', target => events.push('CREATED: ' + target.url())); - context.on('targetchanged', target => events.push('CHANGED: ' + target.url())); - context.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url())); - const page = await context.newPage(); - await page.goto(server.EMPTY_PAGE); - await page.close(); - expect(events).toEqual([ - 'CREATED: about:blank', - `CHANGED: ${server.EMPTY_PAGE}`, - `DESTROYED: ${server.EMPTY_PAGE}` - ]); - await context.close(); - }); - it_fails_ffox('should wait for a target', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - let resolved = false; - const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE); - targetPromise.then(() => resolved = true); - const page = await context.newPage(); - expect(resolved).toBe(false); - await page.goto(server.EMPTY_PAGE); - const target = await targetPromise; - expect(await target.page()).toBe(page); - await context.close(); - }); - it('should timeout waiting for a non-existent target', async function({browser, server}) { - const context = await browser.createIncognitoBrowserContext(); - const error = await context.waitForTarget(target => target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e); - expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); - await context.close(); + expect((await browser.pages()).length).toBe(1); + + const context = await browser.createIncognitoBrowserContext(); + await context.newPage(); + expect((await browser.pages()).length).toBe(2); + expect((await context.pages()).length).toBe(1); + + await context.close(); + expect((await browser.pages()).length).toBe(1); + }); + itFailsFirefox('window.open should use parent tab context', async() => { + const { browser, server } = getTestState(); + + const context = await browser.createIncognitoBrowserContext(); + const page = await context.newPage(); + await page.goto(server.EMPTY_PAGE); + const [popupTarget] = await Promise.all([ + utils.waitEvent(browser, 'targetcreated'), + page.evaluate(url => window.open(url), server.EMPTY_PAGE) + ]); + expect(popupTarget.browserContext()).toBe(context); + await context.close(); + }); + itFailsFirefox('should fire target events', async() => { + const { browser, server } = getTestState(); + + const context = await browser.createIncognitoBrowserContext(); + const events = []; + context.on('targetcreated', target => events.push('CREATED: ' + target.url())); + context.on('targetchanged', target => events.push('CHANGED: ' + target.url())); + context.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url())); + const page = await context.newPage(); + await page.goto(server.EMPTY_PAGE); + await page.close(); + expect(events).toEqual([ + 'CREATED: about:blank', + `CHANGED: ${server.EMPTY_PAGE}`, + `DESTROYED: ${server.EMPTY_PAGE}` + ]); + await context.close(); + }); + itFailsFirefox('should wait for a target', async() => { + const { browser, server } = getTestState(); + + const context = await browser.createIncognitoBrowserContext(); + let resolved = false; + const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE); + targetPromise.then(() => resolved = true); + const page = await context.newPage(); + expect(resolved).toBe(false); + await page.goto(server.EMPTY_PAGE); + const target = await targetPromise; + expect(await target.page()).toBe(page); + await context.close(); + }); + + it('should timeout waiting for a non-existent target', async() => { + const { browser, server, puppeteer } = getTestState(); + + const context = await browser.createIncognitoBrowserContext(); + const error = await context.waitForTarget(target => target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e); + expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); + await context.close(); + }); + + itFailsFirefox('should isolate localStorage and cookies', async() => { + const { browser, server } = getTestState(); + + // Create two incognito contexts. + const context1 = await browser.createIncognitoBrowserContext(); + const context2 = await browser.createIncognitoBrowserContext(); + expect(context1.targets().length).toBe(0); + expect(context2.targets().length).toBe(0); + + // Create a page in first incognito context. + const page1 = await context1.newPage(); + await page1.goto(server.EMPTY_PAGE); + await page1.evaluate(() => { + localStorage.setItem('name', 'page1'); + document.cookie = 'name=page1'; }); - it_fails_ffox('should isolate localStorage and cookies', async function({browser, server}) { - // Create two incognito contexts. - const context1 = await browser.createIncognitoBrowserContext(); - const context2 = await browser.createIncognitoBrowserContext(); - expect(context1.targets().length).toBe(0); - expect(context2.targets().length).toBe(0); - - // Create a page in first incognito context. - const page1 = await context1.newPage(); - await page1.goto(server.EMPTY_PAGE); - await page1.evaluate(() => { - localStorage.setItem('name', 'page1'); - document.cookie = 'name=page1'; - }); - - expect(context1.targets().length).toBe(1); - expect(context2.targets().length).toBe(0); - - // Create a page in second incognito context. - const page2 = await context2.newPage(); - await page2.goto(server.EMPTY_PAGE); - await page2.evaluate(() => { - localStorage.setItem('name', 'page2'); - document.cookie = 'name=page2'; - }); - - expect(context1.targets().length).toBe(1); - expect(context1.targets()[0]).toBe(page1.target()); - expect(context2.targets().length).toBe(1); - expect(context2.targets()[0]).toBe(page2.target()); - - // Make sure pages don't share localstorage or cookies. - expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe('page1'); - expect(await page1.evaluate(() => document.cookie)).toBe('name=page1'); - expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe('page2'); - expect(await page2.evaluate(() => document.cookie)).toBe('name=page2'); - - // Cleanup contexts. - await Promise.all([ - context1.close(), - context2.close() - ]); - expect(browser.browserContexts().length).toBe(1); + + expect(context1.targets().length).toBe(1); + expect(context2.targets().length).toBe(0); + + // Create a page in second incognito context. + const page2 = await context2.newPage(); + await page2.goto(server.EMPTY_PAGE); + await page2.evaluate(() => { + localStorage.setItem('name', 'page2'); + document.cookie = 'name=page2'; }); - it_fails_ffox('should work across sessions', async function({browser, server}) { - expect(browser.browserContexts().length).toBe(1); - const context = await browser.createIncognitoBrowserContext(); - expect(browser.browserContexts().length).toBe(2); - const remoteBrowser = await puppeteer.connect({ - browserWSEndpoint: browser.wsEndpoint() - }); - const contexts = remoteBrowser.browserContexts(); - expect(contexts.length).toBe(2); - remoteBrowser.disconnect(); - await context.close(); + + expect(context1.targets().length).toBe(1); + expect(context1.targets()[0]).toBe(page1.target()); + expect(context2.targets().length).toBe(1); + expect(context2.targets()[0]).toBe(page2.target()); + + // Make sure pages don't share localstorage or cookies. + expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe('page1'); + expect(await page1.evaluate(() => document.cookie)).toBe('name=page1'); + expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe('page2'); + expect(await page2.evaluate(() => document.cookie)).toBe('name=page2'); + + // Cleanup contexts. + await Promise.all([ + context1.close(), + context2.close() + ]); + expect(browser.browserContexts().length).toBe(1); + }); + + itFailsFirefox('should work across sessions', async() => { + const { browser, puppeteer} = getTestState(); + + expect(browser.browserContexts().length).toBe(1); + const context = await browser.createIncognitoBrowserContext(); + expect(browser.browserContexts().length).toBe(2); + const remoteBrowser = await puppeteer.connect({ + browserWSEndpoint: browser.wsEndpoint() }); + const contexts = remoteBrowser.browserContexts(); + expect(contexts.length).toBe(2); + remoteBrowser.disconnect(); + await context.close(); }); -}; +}); diff --git a/test/chromiumonly.spec.js b/test/chromiumonly.spec.js index 962a7b04b1414..0aed71b26cbcc 100644 --- a/test/chromiumonly.spec.js +++ b/test/chromiumonly.spec.js @@ -13,116 +13,118 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addLauncherTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describeChromeOnly('Chromium-Specific Launcher tests', function() { + describe('Puppeteer.launch |browserURL| option', function() { + it('should be able to connect using browserUrl, with and without trailing slash', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); - describe('Chromium-Specific Launcher tests', function() { - describe('Puppeteer.launch |browserURL| option', function() { - it('should be able to connect using browserUrl, with and without trailing slash', async({server}) => { - const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { - args: ['--remote-debugging-port=21222'] - })); - const browserURL = 'http://127.0.0.1:21222'; + const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { + args: ['--remote-debugging-port=21222'] + })); + const browserURL = 'http://127.0.0.1:21222'; - const browser1 = await puppeteer.connect({browserURL}); - const page1 = await browser1.newPage(); - expect(await page1.evaluate(() => 7 * 8)).toBe(56); - browser1.disconnect(); + const browser1 = await puppeteer.connect({browserURL}); + const page1 = await browser1.newPage(); + expect(await page1.evaluate(() => 7 * 8)).toBe(56); + browser1.disconnect(); - const browser2 = await puppeteer.connect({browserURL: browserURL + '/'}); - const page2 = await browser2.newPage(); - expect(await page2.evaluate(() => 8 * 7)).toBe(56); - browser2.disconnect(); - originalBrowser.close(); - }); - it('should throw when using both browserWSEndpoint and browserURL', async({server}) => { - const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { - args: ['--remote-debugging-port=21222'] - })); - const browserURL = 'http://127.0.0.1:21222'; + const browser2 = await puppeteer.connect({browserURL: browserURL + '/'}); + const page2 = await browser2.newPage(); + expect(await page2.evaluate(() => 8 * 7)).toBe(56); + browser2.disconnect(); + originalBrowser.close(); + }); + it('should throw when using both browserWSEndpoint and browserURL', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); - let error = null; - await puppeteer.connect({browserURL, browserWSEndpoint: originalBrowser.wsEndpoint()}).catch(e => error = e); - expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport'); + const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { + args: ['--remote-debugging-port=21222'] + })); + const browserURL = 'http://127.0.0.1:21222'; - originalBrowser.close(); - }); - it('should throw when trying to connect to non-existing browser', async({server}) => { - const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { - args: ['--remote-debugging-port=21222'] - })); - const browserURL = 'http://127.0.0.1:32333'; + let error = null; + await puppeteer.connect({browserURL, browserWSEndpoint: originalBrowser.wsEndpoint()}).catch(e => error = e); + expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport'); - let error = null; - await puppeteer.connect({browserURL}).catch(e => error = e); - expect(error.message).toContain('Failed to fetch browser webSocket url from'); - originalBrowser.close(); - }); + originalBrowser.close(); }); + it('should throw when trying to connect to non-existing browser', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); + + const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { + args: ['--remote-debugging-port=21222'] + })); + const browserURL = 'http://127.0.0.1:32333'; - describe('Puppeteer.launch |pipe| option', function() { - it('should support the pipe option', async() => { - const options = Object.assign({pipe: true}, defaultBrowserOptions); - const browser = await puppeteer.launch(options); - expect((await browser.pages()).length).toBe(1); - expect(browser.wsEndpoint()).toBe(''); - const page = await browser.newPage(); - expect(await page.evaluate('11 * 11')).toBe(121); - await page.close(); - await browser.close(); - }); - it('should support the pipe argument', async() => { - const options = Object.assign({}, defaultBrowserOptions); - options.args = ['--remote-debugging-pipe'].concat(options.args || []); - const browser = await puppeteer.launch(options); - expect(browser.wsEndpoint()).toBe(''); - const page = await browser.newPage(); - expect(await page.evaluate('11 * 11')).toBe(121); - await page.close(); - await browser.close(); - }); - it('should fire "disconnected" when closing with pipe', async() => { - const options = Object.assign({pipe: true}, defaultBrowserOptions); - const browser = await puppeteer.launch(options); - const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve)); - // Emulate user exiting browser. - browser.process().kill(); - await disconnectedEventPromise; - }); + let error = null; + await puppeteer.connect({browserURL}).catch(e => error = e); + expect(error.message).toContain('Failed to fetch browser webSocket url from'); + originalBrowser.close(); }); + }); + describe('Puppeteer.launch |pipe| option', function() { + it('should support the pipe option', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); + const options = Object.assign({pipe: true}, defaultBrowserOptions); + const browser = await puppeteer.launch(options); + expect((await browser.pages()).length).toBe(1); + expect(browser.wsEndpoint()).toBe(''); + const page = await browser.newPage(); + expect(await page.evaluate('11 * 11')).toBe(121); + await page.close(); + await browser.close(); + }); + it('should support the pipe argument', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); + const options = Object.assign({}, defaultBrowserOptions); + options.args = ['--remote-debugging-pipe'].concat(options.args || []); + const browser = await puppeteer.launch(options); + expect(browser.wsEndpoint()).toBe(''); + const page = await browser.newPage(); + expect(await page.evaluate('11 * 11')).toBe(121); + await page.close(); + await browser.close(); + }); + it('should fire "disconnected" when closing with pipe', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); + const options = Object.assign({pipe: true}, defaultBrowserOptions); + const browser = await puppeteer.launch(options); + const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve)); + // Emulate user exiting browser. + browser.process().kill(); + await disconnectedEventPromise; + }); }); -}; -module.exports.addPageTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +}); + +describeChromeOnly('Chromium-Specific Page Tests', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + it('Page.setRequestInterception should work with intervention headers', async() => { + const { server, page } = getTestState(); - describe('Chromium-Specific Page Tests', function() { - it('Page.setRequestInterception should work with intervention headers', async({server, page}) => { - server.setRoute('/intervention', (req, res) => res.end(` + server.setRoute('/intervention', (req, res) => res.end(` `)); - server.setRedirect('/intervention.js', '/redirect.js'); - let serverRequest = null; - server.setRoute('/redirect.js', (req, res) => { - serverRequest = req; - res.end('console.log(1);'); - }); - - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - await page.goto(server.PREFIX + '/intervention'); - // Check for feature URL substring rather than https://www.chromestatus.com to - // make it work with Edgium. - expect(serverRequest.headers.intervention).toContain('feature/5718547946799104'); + server.setRedirect('/intervention.js', '/redirect.js'); + let serverRequest = null; + server.setRoute('/redirect.js', (req, res) => { + serverRequest = req; + res.end('console.log(1);'); }); - }); -}; + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + await page.goto(server.PREFIX + '/intervention'); + // Check for feature URL substring rather than https://www.chromestatus.com to + // make it work with Edgium. + expect(serverRequest.headers.intervention).toContain('feature/5718547946799104'); + }); +}); diff --git a/test/click.spec.js b/test/click.spec.js index 1ec8cf926e935..b90d4700fb1ed 100644 --- a/test/click.spec.js +++ b/test/click.spec.js @@ -14,37 +14,44 @@ * limitations under the License. */ +const expect = require('expect'); +const {getTestState,setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils'); const utils = require('./utils'); -module.exports.addTests = function({testRunner, expect, puppeteer}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Page.click', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + it('should click the button', async() => { + const { page, server } = getTestState(); - describe('Page.click', function() { - it('should click the button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); - }); - it_fails_ffox('should click svg', async({page, server}) => { - await page.setContent(` + await page.goto(server.PREFIX + '/input/button.html'); + await page.click('button'); + expect(await page.evaluate(() => result)).toBe('Clicked'); + }); + itFailsFirefox('should click svg', async() => { + const { page } = getTestState(); + + await page.setContent(` `); - await page.click('circle'); - expect(await page.evaluate(() => window.__CLICKED)).toBe(42); - }); - it_fails_ffox('should click the button if window.Node is removed', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => delete window.Node); - await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); - }); - // @see https://github.com/puppeteer/puppeteer/issues/4281 - it_fails_ffox('should click on a span with an inline element inside', async({page, server}) => { - await page.setContent(` + await page.click('circle'); + expect(await page.evaluate(() => window.__CLICKED)).toBe(42); + }); + itFailsFirefox('should click the button if window.Node is removed', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/button.html'); + await page.evaluate(() => delete window.Node); + await page.click('button'); + expect(await page.evaluate(() => result)).toBe('Clicked'); + }); + // @see https://github.com/puppeteer/puppeteer/issues/4281 + itFailsFirefox('should click on a span with an inline element inside', async() => { + const { page } = getTestState(); + + await page.setContent(` `); - await page.click('span'); - expect(await page.evaluate(() => window.CLICKED)).toBe(42); - }); - it('should not throw UnhandledPromiseRejection when page closes', async({page, server}) => { - const newPage = await page.browser().newPage(); - await Promise.all([ - newPage.close(), - newPage.mouse.click(1, 2), - ]).catch(e => {}); - }); - it('should click the button after navigation ', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.click('button'); - await page.goto(server.PREFIX + '/input/button.html'); - await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); - }); - it_fails_ffox('should click with disabled javascript', async({page, server}) => { - await page.setJavaScriptEnabled(false); - await page.goto(server.PREFIX + '/wrappedlink.html'); - await Promise.all([ - page.click('a'), - page.waitForNavigation() - ]); - expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked'); - }); - it_fails_ffox('should click when one of inline box children is outside of viewport', async({page, server}) => { - await page.setContent(` + await page.click('span'); + expect(await page.evaluate(() => window.CLICKED)).toBe(42); + }); + it('should not throw UnhandledPromiseRejection when page closes', async() => { + const { page } = getTestState(); + + const newPage = await page.browser().newPage(); + await Promise.all([ + newPage.close(), + newPage.mouse.click(1, 2), + ]).catch(e => {}); + }); + it('should click the button after navigation ', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/button.html'); + await page.click('button'); + await page.goto(server.PREFIX + '/input/button.html'); + await page.click('button'); + expect(await page.evaluate(() => result)).toBe('Clicked'); + }); + itFailsFirefox('should click with disabled javascript', async() => { + const { page, server } = getTestState(); + + await page.setJavaScriptEnabled(false); + await page.goto(server.PREFIX + '/wrappedlink.html'); + await Promise.all([ + page.click('a'), + page.waitForNavigation() + ]); + expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked'); + }); + itFailsFirefox('should click when one of inline box children is outside of viewport', async() => { + const { page } = getTestState(); + + await page.setContent(` woofdoggo `); - await page.click('span'); - expect(await page.evaluate(() => window.CLICKED)).toBe(42); - }); - it('should select the text by triple clicking', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - const text = 'This is the text that we are going to try to select. Let\'s see how it goes.'; - await page.keyboard.type(text); - await page.click('textarea'); - await page.click('textarea', {clickCount: 2}); - await page.click('textarea', {clickCount: 3}); - expect(await page.evaluate(() => { - const textarea = document.querySelector('textarea'); - return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); - })).toBe(text); - }); - it_fails_ffox('should click offscreen buttons', async({page, server}) => { - await page.goto(server.PREFIX + '/offscreenbuttons.html'); - const messages = []; - page.on('console', msg => messages.push(msg.text())); - for (let i = 0; i < 11; ++i) { - // We might've scrolled to click a button - reset to (0, 0). - await page.evaluate(() => window.scrollTo(0, 0)); - await page.click(`#btn${i}`); - } - expect(messages).toEqual([ - 'button #0 clicked', - 'button #1 clicked', - 'button #2 clicked', - 'button #3 clicked', - 'button #4 clicked', - 'button #5 clicked', - 'button #6 clicked', - 'button #7 clicked', - 'button #8 clicked', - 'button #9 clicked', - 'button #10 clicked' - ]); - }); + await page.click('span'); + expect(await page.evaluate(() => window.CLICKED)).toBe(42); + }); + it('should select the text by triple clicking', async() => { + const { page, server } = getTestState(); - it('should click wrapped links', async({page, server}) => { - await page.goto(server.PREFIX + '/wrappedlink.html'); - await page.click('a'); - expect(await page.evaluate(() => window.__clicked)).toBe(true); - }); + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + const text = 'This is the text that we are going to try to select. Let\'s see how it goes.'; + await page.keyboard.type(text); + await page.click('textarea'); + await page.click('textarea', {clickCount: 2}); + await page.click('textarea', {clickCount: 3}); + expect(await page.evaluate(() => { + const textarea = document.querySelector('textarea'); + return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); + })).toBe(text); + }); + itFailsFirefox('should click offscreen buttons', async() => { + const { page, server } = getTestState(); - it('should click on checkbox input and toggle', async({page, server}) => { - await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => result.check)).toBe(null); - await page.click('input#agree'); - expect(await page.evaluate(() => result.check)).toBe(true); - expect(await page.evaluate(() => result.events)).toEqual([ - 'mouseover', - 'mouseenter', - 'mousemove', - 'mousedown', - 'mouseup', - 'click', - 'input', - 'change', - ]); - await page.click('input#agree'); - expect(await page.evaluate(() => result.check)).toBe(false); - }); + await page.goto(server.PREFIX + '/offscreenbuttons.html'); + const messages = []; + page.on('console', msg => messages.push(msg.text())); + for (let i = 0; i < 11; ++i) { + // We might've scrolled to click a button - reset to (0, 0). + await page.evaluate(() => window.scrollTo(0, 0)); + await page.click(`#btn${i}`); + } + expect(messages).toEqual([ + 'button #0 clicked', + 'button #1 clicked', + 'button #2 clicked', + 'button #3 clicked', + 'button #4 clicked', + 'button #5 clicked', + 'button #6 clicked', + 'button #7 clicked', + 'button #8 clicked', + 'button #9 clicked', + 'button #10 clicked' + ]); + }); - it_fails_ffox('should click on checkbox label and toggle', async({page, server}) => { - await page.goto(server.PREFIX + '/input/checkbox.html'); - expect(await page.evaluate(() => result.check)).toBe(null); - await page.click('label[for="agree"]'); - expect(await page.evaluate(() => result.check)).toBe(true); - expect(await page.evaluate(() => result.events)).toEqual([ - 'click', - 'input', - 'change', - ]); - await page.click('label[for="agree"]'); - expect(await page.evaluate(() => result.check)).toBe(false); - }); + it('should click wrapped links', async() => { + const { page, server } = getTestState(); - it('should fail to click a missing button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - let error = null; - await page.click('button.does-not-exist').catch(e => error = e); - expect(error.message).toBe('No node found for selector: button.does-not-exist'); - }); - // @see https://github.com/puppeteer/puppeteer/issues/161 - it('should not hang with touch-enabled viewports', async({page, server}) => { - await page.setViewport(puppeteer.devices['iPhone 6'].viewport); - await page.mouse.down(); - await page.mouse.move(100, 10); - await page.mouse.up(); - }); - it('should scroll and click the button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.click('#button-5'); - expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked'); - await page.click('#button-80'); - expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked'); - }); - it_fails_ffox('should double click the button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => { - window.double = false; - const button = document.querySelector('button'); - button.addEventListener('dblclick', event => { - window.double = true; - }); - }); - const button = await page.$('button'); - await button.click({ clickCount: 2 }); - expect(await page.evaluate('double')).toBe(true); - expect(await page.evaluate('result')).toBe('Clicked'); - }); - it('should click a partially obscured button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/button.html'); - await page.evaluate(() => { - const button = document.querySelector('button'); - button.textContent = 'Some really long text that will go offscreen'; - button.style.position = 'absolute'; - button.style.left = '368px'; + await page.goto(server.PREFIX + '/wrappedlink.html'); + await page.click('a'); + expect(await page.evaluate(() => window.__clicked)).toBe(true); + }); + + it('should click on checkbox input and toggle', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/checkbox.html'); + expect(await page.evaluate(() => result.check)).toBe(null); + await page.click('input#agree'); + expect(await page.evaluate(() => result.check)).toBe(true); + expect(await page.evaluate(() => result.events)).toEqual([ + 'mouseover', + 'mouseenter', + 'mousemove', + 'mousedown', + 'mouseup', + 'click', + 'input', + 'change', + ]); + await page.click('input#agree'); + expect(await page.evaluate(() => result.check)).toBe(false); + }); + + itFailsFirefox('should click on checkbox label and toggle', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/checkbox.html'); + expect(await page.evaluate(() => result.check)).toBe(null); + await page.click('label[for="agree"]'); + expect(await page.evaluate(() => result.check)).toBe(true); + expect(await page.evaluate(() => result.events)).toEqual([ + 'click', + 'input', + 'change', + ]); + await page.click('label[for="agree"]'); + expect(await page.evaluate(() => result.check)).toBe(false); + }); + + it('should fail to click a missing button', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/button.html'); + let error = null; + await page.click('button.does-not-exist').catch(e => error = e); + expect(error.message).toBe('No node found for selector: button.does-not-exist'); + }); + // @see https://github.com/puppeteer/puppeteer/issues/161 + it('should not hang with touch-enabled viewports', async() => { + const { page, puppeteer } = getTestState(); + + await page.setViewport(puppeteer.devices['iPhone 6'].viewport); + await page.mouse.down(); + await page.mouse.move(100, 10); + await page.mouse.up(); + }); + it('should scroll and click the button', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/scrollable.html'); + await page.click('#button-5'); + expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked'); + await page.click('#button-80'); + expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked'); + }); + itFailsFirefox('should double click the button', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/button.html'); + await page.evaluate(() => { + window.double = false; + const button = document.querySelector('button'); + button.addEventListener('dblclick', event => { + window.double = true; }); - await page.click('button'); - expect(await page.evaluate(() => window.result)).toBe('Clicked'); - }); - it('should click a rotated button', async({page, server}) => { - await page.goto(server.PREFIX + '/input/rotatedButton.html'); - await page.click('button'); - expect(await page.evaluate(() => result)).toBe('Clicked'); - }); - it('should fire contextmenu event on right click', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.click('#button-8', {button: 'right'}); - expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu'); }); - // @see https://github.com/puppeteer/puppeteer/issues/206 - it_fails_ffox('should click links which cause navigation', async({page, server}) => { - await page.setContent(`empty.html`); - // This await should not hang. - await page.click('a'); - }); - it_fails_ffox('should click the button inside an iframe', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setContent('
spacer
'); - await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); - const frame = page.frames()[1]; - const button = await frame.$('button'); - await button.click(); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); - }); - // @see https://github.com/puppeteer/puppeteer/issues/4110 - xit('should click the button with fixed position inside an iframe', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setViewport({width: 500, height: 500}); - await page.setContent('
spacer
'); - await utils.attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html'); - const frame = page.frames()[1]; - await frame.$eval('button', button => button.style.setProperty('position', 'fixed')); - await frame.click('button'); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); - }); - it_fails_ffox('should click the button with deviceScaleFactor set', async({page, server}) => { - await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5}); - expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5); - await page.setContent('
spacer
'); - await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); - const frame = page.frames()[1]; - const button = await frame.$('button'); - await button.click(); - expect(await frame.evaluate(() => window.result)).toBe('Clicked'); + const button = await page.$('button'); + await button.click({ clickCount: 2 }); + expect(await page.evaluate('double')).toBe(true); + expect(await page.evaluate('result')).toBe('Clicked'); + }); + it('should click a partially obscured button', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/button.html'); + await page.evaluate(() => { + const button = document.querySelector('button'); + button.textContent = 'Some really long text that will go offscreen'; + button.style.position = 'absolute'; + button.style.left = '368px'; }); + await page.click('button'); + expect(await page.evaluate(() => window.result)).toBe('Clicked'); + }); + it('should click a rotated button', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/rotatedButton.html'); + await page.click('button'); + expect(await page.evaluate(() => result)).toBe('Clicked'); + }); + it('should fire contextmenu event on right click', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/scrollable.html'); + await page.click('#button-8', {button: 'right'}); + expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu'); + }); + // @see https://github.com/puppeteer/puppeteer/issues/206 + itFailsFirefox('should click links which cause navigation', async() => { + const { page, server } = getTestState(); + + await page.setContent(`empty.html`); + // This await should not hang. + await page.click('a'); + }); + itFailsFirefox('should click the button inside an iframe', async() => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await page.setContent('
spacer
'); + await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); + const frame = page.frames()[1]; + const button = await frame.$('button'); + await button.click(); + expect(await frame.evaluate(() => window.result)).toBe('Clicked'); + }); + // @see https://github.com/puppeteer/puppeteer/issues/4110 + xit('should click the button with fixed position inside an iframe', async() => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await page.setViewport({width: 500, height: 500}); + await page.setContent('
spacer
'); + await utils.attachFrame(page, 'button-test', server.CROSS_PROCESS_PREFIX + '/input/button.html'); + const frame = page.frames()[1]; + await frame.$eval('button', button => button.style.setProperty('position', 'fixed')); + await frame.click('button'); + expect(await frame.evaluate(() => window.result)).toBe('Clicked'); + }); + itFailsFirefox('should click the button with deviceScaleFactor set', async() => { + const { page, server } = getTestState(); + + await page.setViewport({width: 400, height: 400, deviceScaleFactor: 5}); + expect(await page.evaluate(() => window.devicePixelRatio)).toBe(5); + await page.setContent('
spacer
'); + await utils.attachFrame(page, 'button-test', server.PREFIX + '/input/button.html'); + const frame = page.frames()[1]; + const button = await frame.$('button'); + await button.click(); + expect(await frame.evaluate(() => window.result)).toBe('Clicked'); }); -}; +}); diff --git a/test/cookies.spec.js b/test/cookies.spec.js index 39ffb9bedb4a6..61f62e98904df 100644 --- a/test/cookies.spec.js +++ b/test/cookies.spec.js @@ -13,18 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Cookie specs', () => { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Page.cookies', function() { - it('should return no cookies in pristine browser context', async({page, server}) => { + it('should return no cookies in pristine browser context', async() => { + const {page, server} = getTestState(); await page.goto(server.EMPTY_PAGE); expect(await page.cookies()).toEqual([]); }); - it_fails_ffox('should get a cookie', async({page, server}) => { + itFailsFirefox('should get a cookie', async() => { + const {page, server} = getTestState(); await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { document.cookie = 'username=John Doe'; @@ -41,7 +44,8 @@ module.exports.addTests = function({testRunner, expect}) { session: true }]); }); - it('should properly report httpOnly cookie', async({page, server}) => { + it('should properly report httpOnly cookie', async() => { + const {page, server} = getTestState(); server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ';HttpOnly; Path=/'); res.end(); @@ -51,7 +55,8 @@ module.exports.addTests = function({testRunner, expect}) { expect(cookies.length).toBe(1); expect(cookies[0].httpOnly).toBe(true); }); - it('should properly report "Strict" sameSite cookie', async({page, server}) => { + it('should properly report "Strict" sameSite cookie', async() => { + const {page, server} = getTestState(); server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ';SameSite=Strict'); res.end(); @@ -61,7 +66,8 @@ module.exports.addTests = function({testRunner, expect}) { expect(cookies.length).toBe(1); expect(cookies[0].sameSite).toBe('Strict'); }); - it('should properly report "Lax" sameSite cookie', async({page, server}) => { + it('should properly report "Lax" sameSite cookie', async() => { + const {page, server} = getTestState(); server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ';SameSite=Lax'); res.end(); @@ -71,7 +77,8 @@ module.exports.addTests = function({testRunner, expect}) { expect(cookies.length).toBe(1); expect(cookies[0].sameSite).toBe('Lax'); }); - it_fails_ffox('should get multiple cookies', async({page, server}) => { + itFailsFirefox('should get multiple cookies', async() => { + const {page, server} = getTestState(); await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { document.cookie = 'username=John Doe'; @@ -104,7 +111,8 @@ module.exports.addTests = function({testRunner, expect}) { }, ]); }); - it_fails_ffox('should get cookies from multiple urls', async({page, server}) => { + itFailsFirefox('should get cookies from multiple urls', async() => { + const {page} = getTestState(); await page.setCookie({ url: 'https://foo.com', name: 'doggo', @@ -143,9 +151,10 @@ module.exports.addTests = function({testRunner, expect}) { }]); }); }); - describe('Page.setCookie', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setCookie({ name: 'password', @@ -153,7 +162,9 @@ module.exports.addTests = function({testRunner, expect}) { }); expect(await page.evaluate(() => document.cookie)).toEqual('password=123456'); }); - it_fails_ffox('should isolate cookies in browser contexts', async({page, server, browser}) => { + itFailsFirefox('should isolate cookies in browser contexts', async() => { + const { page, server, browser } = getTestState(); + const anotherContext = await browser.createIncognitoBrowserContext(); const anotherPage = await anotherContext.newPage(); @@ -173,7 +184,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(cookies2[0].value).toBe('page2value'); await anotherContext.close(); }); - it_fails_ffox('should set multiple cookies', async({page, server}) => { + itFailsFirefox('should set multiple cookies', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setCookie({ name: 'password', @@ -190,7 +203,9 @@ module.exports.addTests = function({testRunner, expect}) { 'password=123456', ]); }); - it_fails_ffox('should have |expires| set to |-1| for session cookies', async({page, server}) => { + itFailsFirefox('should have |expires| set to |-1| for session cookies', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setCookie({ name: 'password', @@ -200,7 +215,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(cookies[0].session).toBe(true); expect(cookies[0].expires).toBe(-1); }); - it_fails_ffox('should set cookie with reasonable defaults', async({page, server}) => { + itFailsFirefox('should set cookie with reasonable defaults', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setCookie({ name: 'password', @@ -219,7 +236,9 @@ module.exports.addTests = function({testRunner, expect}) { session: true }]); }); - it_fails_ffox('should set a cookie with a path', async({page, server}) => { + itFailsFirefox('should set a cookie with a path', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/grid.html'); await page.setCookie({ name: 'gridcookie', @@ -244,7 +263,9 @@ module.exports.addTests = function({testRunner, expect}) { await page.goto(server.PREFIX + '/grid.html'); expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID'); }); - it('should not set a cookie on a blank page', async function({page}) { + it('should not set a cookie on a blank page', async() => { + const { page } = getTestState(); + await page.goto('about:blank'); let error = null; try { @@ -254,7 +275,9 @@ module.exports.addTests = function({testRunner, expect}) { } expect(error.message).toContain('At least one of the url and domain needs to be specified'); }); - it('should not set a cookie with blank page URL', async function({page, server}) { + it('should not set a cookie with blank page URL', async() => { + const { page, server } = getTestState(); + let error = null; await page.goto(server.EMPTY_PAGE); try { @@ -269,7 +292,9 @@ module.exports.addTests = function({testRunner, expect}) { `Blank page can not have cookie "example-cookie-blank"` ); }); - it('should not set a cookie on a data URL page', async function({page}) { + it('should not set a cookie on a data URL page', async() => { + const { page } = getTestState(); + let error = null; await page.goto('data:,Hello%2C%20World!'); try { @@ -279,7 +304,9 @@ module.exports.addTests = function({testRunner, expect}) { } expect(error.message).toContain('At least one of the url and domain needs to be specified'); }); - it_fails_ffox('should default to setting secure cookie for HTTPS websites', async({page, server}) => { + itFailsFirefox('should default to setting secure cookie for HTTPS websites', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const SECURE_URL = 'https://example.com'; await page.setCookie({ @@ -290,7 +317,9 @@ module.exports.addTests = function({testRunner, expect}) { const [cookie] = await page.cookies(SECURE_URL); expect(cookie.secure).toBe(true); }); - it_fails_ffox('should be able to set unsecure cookie for HTTP website', async({page, server}) => { + itFailsFirefox('should be able to set unsecure cookie for HTTP website', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const HTTP_URL = 'http://example.com'; await page.setCookie({ @@ -301,7 +330,9 @@ module.exports.addTests = function({testRunner, expect}) { const [cookie] = await page.cookies(HTTP_URL); expect(cookie.secure).toBe(false); }); - it_fails_ffox('should set a cookie on a different domain', async({page, server}) => { + itFailsFirefox('should set a cookie on a different domain', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setCookie({ url: 'https://www.example.com', @@ -322,7 +353,9 @@ module.exports.addTests = function({testRunner, expect}) { session: true }]); }); - it_fails_ffox('should set cookies from a frame', async({page, server}) => { + itFailsFirefox('should set cookies from a frame', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/grid.html'); await page.setCookie({name: 'localhost-cookie', value: 'best'}); await page.evaluate(src => { @@ -365,7 +398,9 @@ module.exports.addTests = function({testRunner, expect}) { }); describe('Page.deleteCookie', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setCookie({ name: 'cookie1', @@ -382,4 +417,4 @@ module.exports.addTests = function({testRunner, expect}) { expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3'); }); }); -}; +}); diff --git a/test/coverage-utils.js b/test/coverage-utils.js new file mode 100644 index 0000000000000..34dda1b5cbb72 --- /dev/null +++ b/test/coverage-utils.js @@ -0,0 +1,84 @@ +/** + * Copyright 2020 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* We want to ensure that all of Puppeteer's public API is tested via our unit + * tests but we can't use a tool like Istanbul because the way it instruments code + * unfortunately breaks in Puppeteer where some of that code is then being executed in a browser context. + * + * So instead we maintain this coverage code which does the following: + * * takes every public method that we expect to be tested + * * replaces it with a method that calls the original but also updates a Map of calls + * * in an after() test callback it asserts that every public method was called. + * + * We run this when COVERAGE=1. + */ + +/** + * @param {Map} apiCoverage + * @param {Object} events + * @param {string} className + * @param {!Object} classType + */ +function traceAPICoverage(apiCoverage, events, className, classType) { + className = className.substring(0, 1).toLowerCase() + className.substring(1); + for (const methodName of Reflect.ownKeys(classType.prototype)) { + const method = Reflect.get(classType.prototype, methodName); + if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function') + continue; + apiCoverage.set(`${className}.${methodName}`, false); + Reflect.set(classType.prototype, methodName, function(...args) { + apiCoverage.set(`${className}.${methodName}`, true); + return method.call(this, ...args); + }); + } + + if (events[classType.name]) { + for (const event of Object.values(events[classType.name])) { + if (typeof event !== 'symbol') + apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, false); + } + const method = Reflect.get(classType.prototype, 'emit'); + Reflect.set(classType.prototype, 'emit', function(event, ...args) { + if (typeof event !== 'symbol' && this.listenerCount(event)) + apiCoverage.set(`${className}.emit(${JSON.stringify(event)})`, true); + return method.call(this, event, ...args); + }); + } +} + +module.exports = function() { + const coverageMap = new Map(); + before(() => { + const api = require('../lib/api'); + const events = require('../lib/Events'); + for (const [className, classType] of Object.entries(api)) + traceAPICoverage(coverageMap, events, className, classType); + }); + + after(() => { + const missingMethods = []; + for (const method of coverageMap.keys()) { + if (!coverageMap.get(method)) + missingMethods.push(method); + } + if (missingMethods.length) { + console.error('\nCoverage check failed: not all API methods called. See above ouptut for list of missing methods.'); + console.error(Array.from(missingMethods).join('\n')); + process.exit(1); + } + console.log('\nAll Puppeteer API methods were called. Coverage test passed.\n'); + }); +}; diff --git a/test/coverage.spec.js b/test/coverage.spec.js index 56d6b18c7ee7b..17ac734a4fb8c 100644 --- a/test/coverage.spec.js +++ b/test/coverage.spec.js @@ -14,13 +14,16 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +const expect = require('expect'); +const {getTestState, setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils'); - describe('JSCoverage', function() { - it('should work', async function({page, server}) { +describe('Coverage specs', function() { + describeChromeOnly('JSCoverage', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + it('should work', async() => { + const { page, server } = getTestState(); await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/simple.html', {waitUntil: 'networkidle0'}); const coverage = await page.coverage.stopJSCoverage(); @@ -31,27 +34,35 @@ module.exports.addTests = function({testRunner, expect}) { { start: 35, end: 61 }, ]); }); - it('should report sourceURLs', async function({page, server}) { + it('should report sourceURLs', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); expect(coverage[0].url).toBe('nicename.js'); }); - it('should ignore eval() scripts by default', async function({page, server}) { + it('should ignore eval() scripts by default', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/eval.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(1); }); - it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) { + it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage({reportAnonymousScripts: true}); await page.goto(server.PREFIX + '/jscoverage/eval.html'); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.find(entry => entry.url.startsWith('debugger://'))).not.toBe(null); expect(coverage.length).toBe(2); }); - it('should ignore pptr internal scripts if reportAnonymousScripts is true', async function({page, server}) { + it('should ignore pptr internal scripts if reportAnonymousScripts is true', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage({reportAnonymousScripts: true}); await page.goto(server.EMPTY_PAGE); await page.evaluate('console.log("foo")'); @@ -59,7 +70,9 @@ module.exports.addTests = function({testRunner, expect}) { const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(0); }); - it('should report multiple scripts', async function({page, server}) { + it('should report multiple scripts', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/multiple.html'); const coverage = await page.coverage.stopJSCoverage(); @@ -68,7 +81,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(coverage[0].url).toContain('/jscoverage/script1.js'); expect(coverage[1].url).toContain('/jscoverage/script2.js'); }); - it('should report right ranges', async function({page, server}) { + it('should report right ranges', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/ranges.html'); const coverage = await page.coverage.stopJSCoverage(); @@ -78,7 +93,9 @@ module.exports.addTests = function({testRunner, expect}) { const range = entry.ranges[0]; expect(entry.text.substring(range.start, range.end)).toBe(`console.log('used!');`); }); - it('should report scripts that have no coverage', async function({page, server}) { + it('should report scripts that have no coverage', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/unused.html'); const coverage = await page.coverage.stopJSCoverage(); @@ -87,21 +104,28 @@ module.exports.addTests = function({testRunner, expect}) { expect(entry.url).toContain('unused.html'); expect(entry.ranges.length).toBe(0); }); - it('should work with conditionals', async function({page, server}) { + it('should work with conditionals', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/involved.html'); const coverage = await page.coverage.stopJSCoverage(); expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':/')).toBeGolden('jscoverage-involved.txt'); }); describe('resetOnNavigation', function() { - it('should report scripts across navigations when disabled', async function({page, server}) { + it('should report scripts across navigations when disabled', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage({resetOnNavigation: false}); await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.goto(server.EMPTY_PAGE); const coverage = await page.coverage.stopJSCoverage(); expect(coverage.length).toBe(2); }); - it('should NOT report scripts across navigations when enabled', async function({page, server}) { + + it('should NOT report scripts across navigations when enabled', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); // Enabled by default. await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.goto(server.EMPTY_PAGE); @@ -110,7 +134,9 @@ module.exports.addTests = function({testRunner, expect}) { }); }); // @see https://crbug.com/990945 - xit('should not hang when there is a debugger statement', async function({page, server}) { + xit('should not hang when there is a debugger statement', async() => { + const { page, server } = getTestState(); + await page.coverage.startJSCoverage(); await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { @@ -120,8 +146,13 @@ module.exports.addTests = function({testRunner, expect}) { }); }); - describe('CSSCoverage', function() { - it('should work', async function({page, server}) { + describeChromeOnly('CSSCoverage', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + it('should work', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/simple.html'); const coverage = await page.coverage.stopCSSCoverage(); @@ -133,14 +164,18 @@ module.exports.addTests = function({testRunner, expect}) { const range = coverage[0].ranges[0]; expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }'); }); - it('should report sourceURLs', async function({page, server}) { + it('should report sourceURLs', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(1); expect(coverage[0].url).toBe('nicename.css'); }); - it('should report multiple stylesheets', async function({page, server}) { + it('should report multiple stylesheets', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/multiple.html'); const coverage = await page.coverage.stopCSSCoverage(); @@ -149,7 +184,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); }); - it('should report stylesheets that have no coverage', async function({page, server}) { + it('should report stylesheets that have no coverage', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/unused.html'); const coverage = await page.coverage.stopCSSCoverage(); @@ -157,7 +194,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(coverage[0].url).toBe('unused.css'); expect(coverage[0].ranges.length).toBe(0); }); - it('should work with media queries', async function({page, server}) { + it('should work with media queries', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/media.html'); const coverage = await page.coverage.stopCSSCoverage(); @@ -167,13 +206,17 @@ module.exports.addTests = function({testRunner, expect}) { {start: 17, end: 38} ]); }); - it('should work with complicated usecases', async function({page, server}) { + it('should work with complicated usecases', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/involved.html'); const coverage = await page.coverage.stopCSSCoverage(); expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':/')).toBeGolden('csscoverage-involved.txt'); }); - it('should ignore injected stylesheets', async function({page, server}) { + it('should ignore injected stylesheets', async() => { + const { page } = getTestState(); + await page.coverage.startCSSCoverage(); await page.addStyleTag({content: 'body { margin: 10px;}'}); // trigger style recalc @@ -183,14 +226,18 @@ module.exports.addTests = function({testRunner, expect}) { expect(coverage.length).toBe(0); }); describe('resetOnNavigation', function() { - it('should report stylesheets across navigations', async function({page, server}) { + it('should report stylesheets across navigations', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage({resetOnNavigation: false}); await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.EMPTY_PAGE); const coverage = await page.coverage.stopCSSCoverage(); expect(coverage.length).toBe(2); }); - it('should NOT report scripts across navigations', async function({page, server}) { + it('should NOT report scripts across navigations', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); // Enabled by default. await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.EMPTY_PAGE); @@ -198,7 +245,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(coverage.length).toBe(0); }); }); - it('should work with a recently loaded stylesheet', async function({page, server}) { + it('should work with a recently loaded stylesheet', async() => { + const { page, server } = getTestState(); + await page.coverage.startCSSCoverage(); await page.evaluate(async url => { document.body.textContent = 'hello, world'; @@ -213,4 +262,4 @@ module.exports.addTests = function({testRunner, expect}) { expect(coverage.length).toBe(1); }); }); -}; +}); diff --git a/test/defaultbrowsercontext.spec.js b/test/defaultbrowsercontext.spec.js index 8075ed0f08a2d..aa7b905ed7631 100644 --- a/test/defaultbrowsercontext.spec.js +++ b/test/defaultbrowsercontext.spec.js @@ -13,81 +13,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('DefaultBrowserContext', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + itFailsFirefox('page.cookies() should work', async() => { + const { page, server } = getTestState(); - describe('DefaultBrowserContext', function() { - beforeEach(async state => { - state.browser = await puppeteer.launch(defaultBrowserOptions); - state.page = await state.browser.newPage(); + await page.goto(server.EMPTY_PAGE); + await page.evaluate(() => { + document.cookie = 'username=John Doe'; }); - afterEach(async state => { - await state.browser.close(); - delete state.browser; - delete state.page; - }); - it_fails_ffox('page.cookies() should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => { - document.cookie = 'username=John Doe'; - }); - expect(await page.cookies()).toEqual([{ - name: 'username', - value: 'John Doe', - domain: 'localhost', - path: '/', - expires: -1, - size: 16, - httpOnly: false, - secure: false, - session: true - }]); - }); - it_fails_ffox('page.setCookie() should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setCookie({ - name: 'username', - value: 'John Doe' - }); - expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe'); - expect(await page.cookies()).toEqual([{ - name: 'username', - value: 'John Doe', - domain: 'localhost', - path: '/', - expires: -1, - size: 16, - httpOnly: false, - secure: false, - session: true - }]); + expect(await page.cookies()).toEqual([{ + name: 'username', + value: 'John Doe', + domain: 'localhost', + path: '/', + expires: -1, + size: 16, + httpOnly: false, + secure: false, + session: true + }]); + }); + itFailsFirefox('page.setCookie() should work', async() => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + name: 'username', + value: 'John Doe' }); - it_fails_ffox('page.deleteCookie() should work', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setCookie({ - name: 'cookie1', - value: '1' - }, { - name: 'cookie2', - value: '2' - }); - expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); - await page.deleteCookie({name: 'cookie2'}); - expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); - expect(await page.cookies()).toEqual([{ - name: 'cookie1', - value: '1', - domain: 'localhost', - path: '/', - expires: -1, - size: 8, - httpOnly: false, - secure: false, - session: true - }]); + expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe'); + expect(await page.cookies()).toEqual([{ + name: 'username', + value: 'John Doe', + domain: 'localhost', + path: '/', + expires: -1, + size: 16, + httpOnly: false, + secure: false, + session: true + }]); + }); + itFailsFirefox('page.deleteCookie() should work', async() => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await page.setCookie({ + name: 'cookie1', + value: '1' + }, { + name: 'cookie2', + value: '2' }); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2'); + await page.deleteCookie({name: 'cookie2'}); + expect(await page.evaluate('document.cookie')).toBe('cookie1=1'); + expect(await page.cookies()).toEqual([{ + name: 'cookie1', + value: '1', + domain: 'localhost', + path: '/', + expires: -1, + size: 8, + httpOnly: false, + secure: false, + session: true + }]); }); -}; +}); diff --git a/test/dialog.spec.js b/test/dialog.spec.js index 1af64433e9ed6..06a837f96130e 100644 --- a/test/dialog.spec.js +++ b/test/dialog.spec.js @@ -13,38 +13,42 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const expect = require('expect'); +const {getTestState,setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Page.Events.Dialog', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + it('should fire', async() => { + const { page } = getTestState(); - describe('Page.Events.Dialog', function() { - it('should fire', async({page, server}) => { - page.on('dialog', dialog => { - expect(dialog.type()).toBe('alert'); - expect(dialog.defaultValue()).toBe(''); - expect(dialog.message()).toBe('yo'); - dialog.accept(); - }); - await page.evaluate(() => alert('yo')); + page.on('dialog', dialog => { + expect(dialog.type()).toBe('alert'); + expect(dialog.defaultValue()).toBe(''); + expect(dialog.message()).toBe('yo'); + dialog.accept(); }); - it_fails_ffox('should allow accepting prompts', async({page, server}) => { - page.on('dialog', dialog => { - expect(dialog.type()).toBe('prompt'); - expect(dialog.defaultValue()).toBe('yes.'); - expect(dialog.message()).toBe('question?'); - dialog.accept('answer!'); - }); - const result = await page.evaluate(() => prompt('question?', 'yes.')); - expect(result).toBe('answer!'); + await page.evaluate(() => alert('yo')); + }); + itFailsFirefox('should allow accepting prompts', async() => { + const { page } = getTestState(); + + page.on('dialog', dialog => { + expect(dialog.type()).toBe('prompt'); + expect(dialog.defaultValue()).toBe('yes.'); + expect(dialog.message()).toBe('question?'); + dialog.accept('answer!'); }); - it('should dismiss the prompt', async({page, server}) => { - page.on('dialog', dialog => { - dialog.dismiss(); - }); - const result = await page.evaluate(() => prompt('question?')); - expect(result).toBe(null); + const result = await page.evaluate(() => prompt('question?', 'yes.')); + expect(result).toBe('answer!'); + }); + it('should dismiss the prompt', async() => { + const { page } = getTestState(); + + page.on('dialog', dialog => { + dialog.dismiss(); }); + const result = await page.evaluate(() => prompt('question?')); + expect(result).toBe(null); }); -}; +}); diff --git a/test/elementhandle.spec.js b/test/elementhandle.spec.js index a2ba620d7e266..f18b10eb45754 100644 --- a/test/elementhandle.spec.js +++ b/test/elementhandle.spec.js @@ -14,38 +14,48 @@ * limitations under the License. */ +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); + const utils = require('./utils'); -module.exports.addTests = function({testRunner, expect, CHROME}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('ElementHandle specs', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + describeFailsFirefox('ElementHandle.boundingBox', function() { + it('should work', async() => { + const { page, server } = getTestState(); - describe_fails_ffox('ElementHandle.boundingBox', function() { - it('should work', async({page, server}) => { await page.setViewport({width: 500, height: 500}); await page.goto(server.PREFIX + '/grid.html'); const elementHandle = await page.$('.box:nth-of-type(13)'); const box = await elementHandle.boundingBox(); expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 }); }); - it('should handle nested frames', async({page, server}) => { + it('should handle nested frames', async() => { + const { page, server, isChrome } = getTestState(); + await page.setViewport({width: 500, height: 500}); await page.goto(server.PREFIX + '/frames/nested-frames.html'); const nestedFrame = page.frames()[1].childFrames()[1]; const elementHandle = await nestedFrame.$('div'); const box = await elementHandle.boundingBox(); - if (CHROME) + if (isChrome) expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 }); else expect(box).toEqual({ x: 28, y: 182, width: 254, height: 18 }); }); - it('should return null for invisible elements', async({page, server}) => { + it('should return null for invisible elements', async() => { + const { page } = getTestState(); + await page.setContent('
hi
'); const element = await page.$('div'); expect(await element.boundingBox()).toBe(null); }); - it('should force a layout', async({page, server}) => { + it('should force a layout', async() => { + const { page } = getTestState(); + await page.setViewport({ width: 500, height: 500 }); await page.setContent('
hello
'); const elementHandle = await page.$('div'); @@ -53,7 +63,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { const box = await elementHandle.boundingBox(); expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 }); }); - it('should work with SVG nodes', async({page, server}) => { + it('should work with SVG nodes', async() => { + const { page } = getTestState(); + await page.setContent(` @@ -69,8 +81,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('ElementHandle.boxModel', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('ElementHandle.boxModel', function() { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/resetcss.html'); // Step 1: Add Frame and position it absolutely. @@ -125,7 +139,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - it('should return null for invisible elements', async({page, server}) => { + it('should return null for invisible elements', async() => { + const { page } = getTestState(); + await page.setContent('
hi
'); const element = await page.$('div'); expect(await element.boxModel()).toBe(null); @@ -133,7 +149,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('ElementHandle.contentFrame', function() { - it_fails_ffox('should work', async({page,server}) => { + itFailsFirefox('should work', async() => { + const { page,server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const elementHandle = await page.$('#frame1'); @@ -143,26 +161,34 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('ElementHandle.click', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await button.click(); expect(await page.evaluate(() => result)).toBe('Clicked'); }); - it('should work for Shadow DOM v1', async({page, server}) => { + it('should work for Shadow DOM v1', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/shadow.html'); const buttonHandle = await page.evaluateHandle(() => button); await buttonHandle.click(); expect(await page.evaluate(() => clicked)).toBe(true); }); - it('should work for TextNodes', async({page, server}) => { + it('should work for TextNodes', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/button.html'); const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild); let error = null; await buttonTextNode.click().catch(err => error = err); expect(error.message).toBe('Node is not of type HTMLElement'); }); - it('should throw for detached nodes', async({page, server}) => { + it('should throw for detached nodes', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.evaluate(button => button.remove(), button); @@ -170,21 +196,27 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { await button.click().catch(err => error = err); expect(error.message).toBe('Node is detached from document'); }); - it('should throw for hidden nodes', async({page, server}) => { + it('should throw for hidden nodes', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.evaluate(button => button.style.display = 'none', button); const error = await button.click().catch(err => err); expect(error.message).toBe('Node is either not visible or not an HTMLElement'); }); - it('should throw for recursively hidden nodes', async({page, server}) => { + it('should throw for recursively hidden nodes', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.evaluate(button => button.parentElement.style.display = 'none', button); const error = await button.click().catch(err => err); expect(error.message).toBe('Node is either not visible or not an HTMLElement'); }); - it_fails_ffox('should throw for
elements', async({page, server}) => { + itFailsFirefox('should throw for
elements', async() => { + const { page } = getTestState(); + await page.setContent('hello
goodbye'); const br = await page.$('br'); const error = await br.click().catch(err => err); @@ -193,7 +225,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('ElementHandle.hover', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/scrollable.html'); const button = await page.$('#button-6'); await button.hover(); @@ -202,7 +236,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('ElementHandle.isIntersectingViewport', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/offscreenbuttons.html'); for (let i = 0; i < 11; ++i) { const button = await page.$('#btn' + i); @@ -212,4 +248,4 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { } }); }); -}; +}); diff --git a/test/emulation.spec.js b/test/emulation.spec.js index e99466ae09de3..2465286f26c36 100644 --- a/test/emulation.spec.js +++ b/test/emulation.spec.js @@ -14,21 +14,33 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, puppeteer}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - const iPhone = puppeteer.devices['iPhone 6']; - const iPhoneLandscape = puppeteer.devices['iPhone 6 landscape']; +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); + +describe('Emulation', () => { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + let iPhone; + let iPhoneLandscape; + + before(() => { + const {puppeteer} = getTestState(); + iPhone = puppeteer.devices['iPhone 6']; + iPhoneLandscape = puppeteer.devices['iPhone 6 landscape']; + }); describe('Page.viewport', function() { - it('should get the proper viewport size', async({page, server}) => { + + it('should get the proper viewport size', async() => { + const { page } = getTestState(); + expect(page.viewport()).toEqual({width: 800, height: 600}); await page.setViewport({width: 123, height: 456}); expect(page.viewport()).toEqual({width: 123, height: 456}); }); - it('should support mobile emulation', async({page, server}) => { + it('should support mobile emulation', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => window.innerWidth)).toBe(800); await page.setViewport(iPhone.viewport); @@ -36,7 +48,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { await page.setViewport({width: 400, height: 300}); expect(await page.evaluate(() => window.innerWidth)).toBe(400); }); - it_fails_ffox('should support touch emulation', async({page, server}) => { + itFailsFirefox('should support touch emulation', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); await page.setViewport(iPhone.viewport); @@ -58,19 +72,25 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { return promise; } }); - it_fails_ffox('should be detectable by Modernizr', async({page, server}) => { + itFailsFirefox('should be detectable by Modernizr', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/detect-touch.html'); expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO'); await page.setViewport(iPhone.viewport); await page.goto(server.PREFIX + '/detect-touch.html'); expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); }); - it_fails_ffox('should detect touch when applying viewport with touches', async({page, server}) => { + itFailsFirefox('should detect touch when applying viewport with touches', async() => { + const { page, server } = getTestState(); + await page.setViewport({ width: 800, height: 600, hasTouch: true }); await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true); }); - it_fails_ffox('should support landscape emulation', async({page, server}) => { + itFailsFirefox('should support landscape emulation', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary'); await page.setViewport(iPhoneLandscape.viewport); @@ -81,13 +101,17 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); describe('Page.emulate', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/mobile.html'); await page.emulate(iPhone); expect(await page.evaluate(() => window.innerWidth)).toBe(375); expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone'); }); - it('should support clicking', async({page, server}) => { + it('should support clicking', async() => { + const { page, server } = getTestState(); + await page.emulate(iPhone); await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); @@ -98,7 +122,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); describe('Page.emulateMedia [deprecated]', function() { - /* emulateMedia is deprecated in favour of emulateMediaType but we + /* emulateMedia is deprecated in favour of emulateMediaType but we * don't want to remove it from Puppeteer just yet. We can't check * that emulateMedia === emulateMediaType because when running tests * with COVERAGE=1 the methods get rewritten. So instead we @@ -108,7 +132,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { * If you update these tests, you should update emulateMediaType's * tests, and vice-versa. */ - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page } = getTestState(); + expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); await page.emulateMedia('print'); @@ -118,7 +144,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); }); - it('should throw in case of bad argument', async({page, server}) => { + it('should throw in case of bad argument', async() => { + const { page } = getTestState(); + let error = null; await page.emulateMedia('bad').catch(e => error = e); expect(error.message).toBe('Unsupported media type: bad'); @@ -126,10 +154,12 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); describe('Page.emulateMediaType', function() { - /* NOTE! Updating these tests? Update the emulateMedia tests above + /* NOTE! Updating these tests? Update the emulateMedia tests above * too (and see the big comment for why we have these duplicated). */ - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page } = getTestState(); + expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); await page.emulateMediaType('print'); @@ -139,7 +169,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); }); - it('should throw in case of bad argument', async({page, server}) => { + it('should throw in case of bad argument', async() => { + const { page } = getTestState(); + let error = null; await page.emulateMediaType('bad').catch(e => error = e); expect(error.message).toBe('Unsupported media type: bad'); @@ -147,7 +179,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); describe('Page.emulateMediaFeatures', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page } = getTestState(); + await page.emulateMediaFeatures([ { name: 'prefers-reduced-motion', value: 'reduce' }, ]); @@ -175,15 +209,19 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false); }); - it('should throw in case of bad argument', async({page, server}) => { + it('should throw in case of bad argument', async() => { + const { page } = getTestState(); + let error = null; await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e); expect(error.message).toBe('Unsupported media feature: bad'); }); }); - describe_fails_ffox('Page.emulateTimezone', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.emulateTimezone', function() { + it('should work', async() => { + const { page } = getTestState(); + page.evaluate(() => { globalThis.date = new Date(1479579154987); }); @@ -200,7 +238,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)'); }); - it('should throw for invalid timezone IDs', async({page, server}) => { + it('should throw for invalid timezone IDs', async() => { + const { page } = getTestState(); + let error = null; await page.emulateTimezone('Foo/Bar').catch(e => error = e); expect(error.message).toBe('Invalid timezone ID: Foo/Bar'); @@ -209,4 +249,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); -}; +}); diff --git a/test/evaluation.spec.js b/test/evaluation.spec.js index 545452d6eac21..f2c0896d1cfab 100644 --- a/test/evaluation.spec.js +++ b/test/evaluation.spec.js @@ -15,59 +15,85 @@ */ const utils = require('./utils'); +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); const bigint = typeof BigInt !== 'undefined'; -module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Evaluation specs', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Page.evaluate', function() { - it('should work', async({page, server}) => { + + it('should work', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => 7 * 3); expect(result).toBe(21); }); - (bigint ? it_fails_ffox : xit)('should transfer BigInt', async({page, server}) => { + (bigint ? itFailsFirefox : xit)('should transfer BigInt', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a, BigInt(42)); expect(result).toBe(BigInt(42)); }); - it_fails_ffox('should transfer NaN', async({page, server}) => { + itFailsFirefox('should transfer NaN', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a, NaN); expect(Object.is(result, NaN)).toBe(true); }); - it_fails_ffox('should transfer -0', async({page, server}) => { + itFailsFirefox('should transfer -0', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a, -0); expect(Object.is(result, -0)).toBe(true); }); - it_fails_ffox('should transfer Infinity', async({page, server}) => { + itFailsFirefox('should transfer Infinity', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a, Infinity); expect(Object.is(result, Infinity)).toBe(true); }); - it_fails_ffox('should transfer -Infinity', async({page, server}) => { + itFailsFirefox('should transfer -Infinity', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a, -Infinity); expect(Object.is(result, -Infinity)).toBe(true); }); - it('should transfer arrays', async({page, server}) => { + it('should transfer arrays', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a, [1, 2, 3]); expect(result).toEqual([1,2,3]); }); - it('should transfer arrays as arrays, not objects', async({page, server}) => { + it('should transfer arrays as arrays, not objects', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => Array.isArray(a), [1, 2, 3]); expect(result).toBe(true); }); - it('should modify global environment', async({page}) => { + it('should modify global environment', async() => { + const { page } = getTestState(); + await page.evaluate(() => window.globalVar = 123); expect(await page.evaluate('globalVar')).toBe(123); }); - it('should evaluate in the page context', async({page, server}) => { + it('should evaluate in the page context', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/global-var.html'); expect(await page.evaluate('globalVar')).toBe(123); }); - it_fails_ffox('should return undefined for objects with symbols', async({page, server}) => { + itFailsFirefox('should return undefined for objects with symbols', async() => { + const { page } = getTestState(); + expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined); }); - it('should work with function shorthands', async({page, server}) => { + it('should work with function shorthands', async() => { + const { page } = getTestState(); + const a = { sum(a, b) { return a + b; }, @@ -76,11 +102,15 @@ module.exports.addTests = function({testRunner, expect}) { expect(await page.evaluate(a.sum, 1, 2)).toBe(3); expect(await page.evaluate(a.mult, 2, 4)).toBe(8); }); - it('should work with unicode chars', async({page, server}) => { + it('should work with unicode chars', async() => { + const { page } = getTestState(); + const result = await page.evaluate(a => a['中文字符'], {'中文字符': 42}); expect(result).toBe(42); }); - it_fails_ffox('should throw when evaluation triggers reload', async({page, server}) => { + itFailsFirefox('should throw when evaluation triggers reload', async() => { + const { page } = getTestState(); + let error = null; await page.evaluate(() => { location.reload(); @@ -88,11 +118,15 @@ module.exports.addTests = function({testRunner, expect}) { }).catch(e => error = e); expect(error.message).toContain('Protocol error'); }); - it('should await promise', async({page, server}) => { + it('should await promise', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => Promise.resolve(8 * 7)); expect(result).toBe(56); }); - it('should work right after framenavigated', async({page, server}) => { + it('should work right after framenavigated', async() => { + const { page, server } = getTestState(); + let frameEvaluation = null; page.on('framenavigated', async frame => { frameEvaluation = frame.evaluate(() => 6 * 7); @@ -100,7 +134,9 @@ module.exports.addTests = function({testRunner, expect}) { await page.goto(server.EMPTY_PAGE); expect(await frameEvaluation).toBe(42); }); - it_fails_ffox('should work from-inside an exposed function', async({page, server}) => { + itFailsFirefox('should work from-inside an exposed function', async() => { + const { page } = getTestState(); + // Setup inpage callback, which calls Page.evaluate await page.exposeFunction('callController', async function(a, b) { return await page.evaluate((a, b) => a * b, a, b); @@ -110,61 +146,87 @@ module.exports.addTests = function({testRunner, expect}) { }); expect(result).toBe(27); }); - it('should reject promise with exception', async({page, server}) => { + it('should reject promise with exception', async() => { + const { page } = getTestState(); + let error = null; await page.evaluate(() => not_existing_object.property).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('not_existing_object'); }); - it('should support thrown strings as error messages', async({page, server}) => { + it('should support thrown strings as error messages', async() => { + const { page } = getTestState(); + let error = null; await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('qwerty'); }); - it('should support thrown numbers as error messages', async({page, server}) => { + it('should support thrown numbers as error messages', async() => { + const { page } = getTestState(); + let error = null; await page.evaluate(() => { throw 100500; }).catch(e => error = e); expect(error).toBeTruthy(); expect(error.message).toContain('100500'); }); - it('should return complex objects', async({page, server}) => { + it('should return complex objects', async() => { + const { page } = getTestState(); + const object = {foo: 'bar!'}; const result = await page.evaluate(a => a, object); expect(result).not.toBe(object); expect(result).toEqual(object); }); - (bigint ? it_fails_ffox : xit)('should return BigInt', async({page, server}) => { + (bigint ? itFailsFirefox : xit)('should return BigInt', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => BigInt(42)); expect(result).toBe(BigInt(42)); }); - it_fails_ffox('should return NaN', async({page, server}) => { + itFailsFirefox('should return NaN', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => NaN); expect(Object.is(result, NaN)).toBe(true); }); - it_fails_ffox('should return -0', async({page, server}) => { + itFailsFirefox('should return -0', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => -0); expect(Object.is(result, -0)).toBe(true); }); - it_fails_ffox('should return Infinity', async({page, server}) => { + itFailsFirefox('should return Infinity', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => Infinity); expect(Object.is(result, Infinity)).toBe(true); }); - it_fails_ffox('should return -Infinity', async({page, server}) => { + itFailsFirefox('should return -Infinity', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => -Infinity); expect(Object.is(result, -Infinity)).toBe(true); }); - it('should accept "undefined" as one of multiple parameters', async({page, server}) => { + it('should accept "undefined" as one of multiple parameters', async() => { + const { page } = getTestState(); + const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo'); expect(result).toBe(true); }); - it('should properly serialize null fields', async({page}) => { + it('should properly serialize null fields', async() => { + const { page } = getTestState(); + expect(await page.evaluate(() => ({a: undefined}))).toEqual({}); }); - it_fails_ffox('should return undefined for non-serializable objects', async({page, server}) => { + itFailsFirefox('should return undefined for non-serializable objects', async() => { + const { page } = getTestState(); + expect(await page.evaluate(() => window)).toBe(undefined); }); - it_fails_ffox('should fail for circular object', async({page, server}) => { + itFailsFirefox('should fail for circular object', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => { const a = {}; const b = {a}; @@ -173,7 +235,9 @@ module.exports.addTests = function({testRunner, expect}) { }); expect(result).toBe(undefined); }); - it_fails_ffox('should be able to throw a tricky error', async({page, server}) => { + itFailsFirefox('should be able to throw a tricky error', async() => { + const { page } = getTestState(); + const windowHandle = await page.evaluateHandle(() => window); const errorText = await windowHandle.jsonValue().catch(e => e.message); const error = await page.evaluate(errorText => { @@ -181,25 +245,35 @@ module.exports.addTests = function({testRunner, expect}) { }, errorText).catch(e => e); expect(error.message).toContain(errorText); }); - it('should accept a string', async({page, server}) => { + it('should accept a string', async() => { + const { page } = getTestState(); + const result = await page.evaluate('1 + 2'); expect(result).toBe(3); }); - it('should accept a string with semi colons', async({page, server}) => { + it('should accept a string with semi colons', async() => { + const { page } = getTestState(); + const result = await page.evaluate('1 + 5;'); expect(result).toBe(6); }); - it('should accept a string with comments', async({page, server}) => { + it('should accept a string with comments', async() => { + const { page } = getTestState(); + const result = await page.evaluate('2 + 5;\n// do some math!'); expect(result).toBe(7); }); - it_fails_ffox('should accept element handle as an argument', async({page, server}) => { + itFailsFirefox('should accept element handle as an argument', async() => { + const { page } = getTestState(); + await page.setContent('
42
'); const element = await page.$('section'); const text = await page.evaluate(e => e.textContent, element); expect(text).toBe('42'); }); - it_fails_ffox('should throw if underlying element was disposed', async({page, server}) => { + itFailsFirefox('should throw if underlying element was disposed', async() => { + const { page } = getTestState(); + await page.setContent('
39
'); const element = await page.$('section'); expect(element).toBeTruthy(); @@ -208,7 +282,9 @@ module.exports.addTests = function({testRunner, expect}) { await page.evaluate(e => e.textContent, element).catch(e => error = e); expect(error.message).toContain('JSHandle is disposed'); }); - it_fails_ffox('should throw if elementHandles are from other frames', async({page, server}) => { + itFailsFirefox('should throw if elementHandles are from other frames', async() => { + const { page, server } = getTestState(); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const bodyHandle = await page.frames()[1].$('body'); let error = null; @@ -216,7 +292,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(error).toBeTruthy(); expect(error.message).toContain('JSHandles can be evaluated only in the context they were created'); }); - it_fails_ffox('should simulate a user gesture', async({page, server}) => { + itFailsFirefox('should simulate a user gesture', async() => { + const { page } = getTestState(); + const result = await page.evaluate(() => { document.body.appendChild(document.createTextNode('test')); document.execCommand('selectAll'); @@ -224,7 +302,9 @@ module.exports.addTests = function({testRunner, expect}) { }); expect(result).toBe(true); }); - it_fails_ffox('should throw a nice error after a navigation', async({page, server}) => { + itFailsFirefox('should throw a nice error after a navigation', async() => { + const { page } = getTestState(); + const executionContext = await page.mainFrame().executionContext(); await Promise.all([ @@ -234,7 +314,9 @@ module.exports.addTests = function({testRunner, expect}) { const error = await executionContext.evaluate(() => null).catch(e => e); expect(error.message).toContain('navigation'); }); - it_fails_ffox('should not throw an error when evaluation does a navigation', async({page, server}) => { + itFailsFirefox('should not throw an error when evaluation does a navigation', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/one-style.html'); const result = await page.evaluate(() => { window.location = '/empty.html'; @@ -242,11 +324,15 @@ module.exports.addTests = function({testRunner, expect}) { }); expect(result).toEqual([42]); }); - it_fails_ffox('should transfer 100Mb of data from page to node.js', async({page, server}) => { + itFailsFirefox('should transfer 100Mb of data from page to node.js', async function() { + const { page } = getTestState(); + const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a')); expect(a.length).toBe(100 * 1024 * 1024); }); - it('should throw error with detailed information on exception inside promise ', async({page, server}) => { + it('should throw error with detailed information on exception inside promise ', async() => { + const { page } = getTestState(); + let error = null; await page.evaluate(() => new Promise(() => { throw new Error('Error in promise'); @@ -255,15 +341,19 @@ module.exports.addTests = function({testRunner, expect}) { }); }); - describe_fails_ffox('Page.evaluateOnNewDocument', function() { - it('should evaluate before anything else on the page', async({page, server}) => { + describeFailsFirefox('Page.evaluateOnNewDocument', function() { + it('should evaluate before anything else on the page', async() => { + const { page, server } = getTestState(); + await page.evaluateOnNewDocument(function(){ window.injected = 123; }); await page.goto(server.PREFIX + '/tamperable.html'); expect(await page.evaluate(() => window.result)).toBe(123); }); - it('should work with CSP', async({page, server}) => { + it('should work with CSP', async() => { + const { page, server } = getTestState(); + server.setCSP('/empty.html', 'script-src ' + server.PREFIX); await page.evaluateOnNewDocument(function(){ window.injected = 123; @@ -278,7 +368,9 @@ module.exports.addTests = function({testRunner, expect}) { }); describe('Frame.evaluate', function() { - it_fails_ffox('should have different execution contexts', async({page, server}) => { + itFailsFirefox('should have different execution contexts', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(page.frames().length).toBe(2); @@ -287,13 +379,17 @@ module.exports.addTests = function({testRunner, expect}) { expect(await page.frames()[0].evaluate(() => window.FOO)).toBe('foo'); expect(await page.frames()[1].evaluate(() => window.FOO)).toBe('bar'); }); - it_fails_ffox('should have correct execution contexts', async({page, server}) => { + itFailsFirefox('should have correct execution contexts', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames().length).toBe(2); expect(await page.frames()[0].evaluate(() => document.body.textContent.trim())).toBe(''); expect(await page.frames()[1].evaluate(() => document.body.textContent.trim())).toBe(`Hi, I'm frame`); }); - it('should execute after cross-site navigation', async({page, server}) => { + it('should execute after cross-site navigation', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost'); @@ -301,4 +397,4 @@ module.exports.addTests = function({testRunner, expect}) { expect(await mainFrame.evaluate(() => window.location.href)).toContain('127'); }); }); -}; +}); diff --git a/test/fixtures.spec.js b/test/fixtures.spec.js index 102ec49e16854..57daaeaba054f 100644 --- a/test/fixtures.spec.js +++ b/test/fixtures.spec.js @@ -14,59 +14,62 @@ * limitations under the License. */ +const expect = require('expect'); +const {getTestState} = require('./mocha-utils'); + const path = require('path'); -module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, puppeteerPath}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Fixtures', function() { + itFailsFirefox('dumpio option should work with pipe option ', async() => { + const { defaultBrowserOptions, puppeteerPath } = getTestState(); - describe('Fixtures', function() { - it_fails_ffox('dumpio option should work with pipe option ', async({server}) => { - let dumpioData = ''; - const {spawn} = require('child_process'); - const options = Object.assign({}, defaultBrowserOptions, {pipe: true, dumpio: true}); - const res = spawn('node', - [path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]); - res.stderr.on('data', data => dumpioData += data.toString('utf8')); - await new Promise(resolve => res.on('close', resolve)); - expect(dumpioData).toContain('message from dumpio'); - }); - it('should dump browser process stderr', async({server}) => { - let dumpioData = ''; - const {spawn} = require('child_process'); - const options = Object.assign({}, defaultBrowserOptions, {dumpio: true}); - const res = spawn('node', - [path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]); - res.stderr.on('data', data => dumpioData += data.toString('utf8')); - await new Promise(resolve => res.on('close', resolve)); - expect(dumpioData).toContain('DevTools listening on ws://'); + let dumpioData = ''; + const {spawn} = require('child_process'); + const options = Object.assign({}, defaultBrowserOptions, {pipe: true, dumpio: true}); + const res = spawn('node', + [path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]); + res.stderr.on('data', data => dumpioData += data.toString('utf8')); + await new Promise(resolve => res.on('close', resolve)); + expect(dumpioData).toContain('message from dumpio'); + }); + it('should dump browser process stderr', async() => { + const { defaultBrowserOptions, puppeteerPath} = getTestState(); + + let dumpioData = ''; + const {spawn} = require('child_process'); + const options = Object.assign({}, defaultBrowserOptions, {dumpio: true}); + const res = spawn('node', + [path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]); + res.stderr.on('data', data => dumpioData += data.toString('utf8')); + await new Promise(resolve => res.on('close', resolve)); + expect(dumpioData).toContain('DevTools listening on ws://'); + }); + it('should close the browser when the node process closes', async() => { + const { defaultBrowserOptions, puppeteerPath, puppeteer} = getTestState(); + + const {spawn, execSync} = require('child_process'); + const options = Object.assign({}, defaultBrowserOptions, { + // Disable DUMPIO to cleanly read stdout. + dumpio: false, }); - it('should close the browser when the node process closes', async({ server }) => { - const {spawn, execSync} = require('child_process'); - const options = Object.assign({}, defaultBrowserOptions, { - // Disable DUMPIO to cleanly read stdout. - dumpio: false, - }); - const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), puppeteerPath, JSON.stringify(options)]); - let wsEndPointCallback; - const wsEndPointPromise = new Promise(x => wsEndPointCallback = x); - let output = ''; - res.stdout.on('data', data => { - output += data; - if (output.indexOf('\n')) - wsEndPointCallback(output.substring(0, output.indexOf('\n'))); - }); - const browser = await puppeteer.connect({ browserWSEndpoint: await wsEndPointPromise }); - const promises = [ - new Promise(resolve => browser.once('disconnected', resolve)), - new Promise(resolve => res.on('close', resolve)) - ]; - if (process.platform === 'win32') - execSync(`taskkill /pid ${res.pid} /T /F`); - else - process.kill(res.pid); - await Promise.all(promises); + const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), puppeteerPath, JSON.stringify(options)]); + let wsEndPointCallback; + const wsEndPointPromise = new Promise(x => wsEndPointCallback = x); + let output = ''; + res.stdout.on('data', data => { + output += data; + if (output.indexOf('\n')) + wsEndPointCallback(output.substring(0, output.indexOf('\n'))); }); + const browser = await puppeteer.connect({ browserWSEndpoint: await wsEndPointPromise }); + const promises = [ + new Promise(resolve => browser.once('disconnected', resolve)), + new Promise(resolve => res.on('close', resolve)) + ]; + if (process.platform === 'win32') + execSync(`taskkill /pid ${res.pid} /T /F`); + else + process.kill(res.pid); + await Promise.all(promises); }); -}; +}); diff --git a/test/frame.spec.js b/test/frame.spec.js index fac89dea3228e..937395e239ac1 100644 --- a/test/frame.spec.js +++ b/test/frame.spec.js @@ -15,14 +15,17 @@ */ const utils = require('./utils'); +const expect = require('expect'); +const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Frame specs', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Frame.executionContext', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(page.frames().length).toBe(2); @@ -49,7 +52,9 @@ module.exports.addTests = function({testRunner, expect}) { }); describe('Frame.evaluateHandle', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); const windowHandle = await mainFrame.evaluateHandle(() => window); @@ -58,7 +63,9 @@ module.exports.addTests = function({testRunner, expect}) { }); describe('Frame.evaluate', function() { - it_fails_ffox('should throw for detached frames', async({page, server}) => { + itFailsFirefox('should throw for detached frames', async() => { + const { page, server } = getTestState(); + const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.detachFrame(page, 'frame1'); let error = null; @@ -68,7 +75,9 @@ module.exports.addTests = function({testRunner, expect}) { }); describe('Frame Management', function() { - it_fails_ffox('should handle nested frames', async({page, server}) => { + itFailsFirefox('should handle nested frames', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/nested-frames.html'); expect(utils.dumpFrames(page.mainFrame())).toEqual([ 'http://localhost:/frames/nested-frames.html', @@ -78,7 +87,9 @@ module.exports.addTests = function({testRunner, expect}) { ' http://localhost:/frames/frame.html (aframe)' ]); }); - it_fails_ffox('should send events when frames are manipulated dynamically', async({page, server}) => { + itFailsFirefox('should send events when frames are manipulated dynamically', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); // validate frameattached events const attachedFrames = []; @@ -101,7 +112,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(detachedFrames.length).toBe(1); expect(detachedFrames[0].isDetached()).toBe(true); }); - it_fails_ffox('should send "framenavigated" when navigating on anchor URLs', async({page, server}) => { + itFailsFirefox('should send "framenavigated" when navigating on anchor URLs', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await Promise.all([ page.goto(server.EMPTY_PAGE + '#foo'), @@ -109,20 +122,26 @@ module.exports.addTests = function({testRunner, expect}) { ]); expect(page.url()).toBe(server.EMPTY_PAGE + '#foo'); }); - it('should persist mainFrame on cross-process navigation', async({page, server}) => { + it('should persist mainFrame on cross-process navigation', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const mainFrame = page.mainFrame(); await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); expect(page.mainFrame() === mainFrame).toBeTruthy(); }); - it('should not send attach/detach events for main frame', async({page, server}) => { + it('should not send attach/detach events for main frame', async() => { + const { page, server } = getTestState(); + let hasEvents = false; page.on('frameattached', frame => hasEvents = true); page.on('framedetached', frame => hasEvents = true); await page.goto(server.EMPTY_PAGE); expect(hasEvents).toBe(false); }); - it_fails_ffox('should detach child frames on navigation', async({page, server}) => { + itFailsFirefox('should detach child frames on navigation', async() => { + const { page, server } = getTestState(); + let attachedFrames = []; let detachedFrames = []; let navigatedFrames = []; @@ -142,7 +161,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(detachedFrames.length).toBe(4); expect(navigatedFrames.length).toBe(1); }); - it_fails_ffox('should support framesets', async({page, server}) => { + itFailsFirefox('should support framesets', async() => { + const { page, server } = getTestState(); + let attachedFrames = []; let detachedFrames = []; let navigatedFrames = []; @@ -162,7 +183,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(detachedFrames.length).toBe(4); expect(navigatedFrames.length).toBe(1); }); - it_fails_ffox('should report frame from-inside shadow DOM', async({page, server}) => { + itFailsFirefox('should report frame from-inside shadow DOM', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/shadow.html'); await page.evaluate(async url => { const frame = document.createElement('iframe'); @@ -173,7 +196,9 @@ module.exports.addTests = function({testRunner, expect}) { expect(page.frames().length).toBe(2); expect(page.frames()[1].url()).toBe(server.EMPTY_PAGE); }); - it_fails_ffox('should report frame.name()', async({page, server}) => { + itFailsFirefox('should report frame.name()', async() => { + const { page, server } = getTestState(); + await utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE); await page.evaluate(url => { const frame = document.createElement('iframe'); @@ -186,14 +211,18 @@ module.exports.addTests = function({testRunner, expect}) { expect(page.frames()[1].name()).toBe('theFrameId'); expect(page.frames()[2].name()).toBe('theFrameName'); }); - it_fails_ffox('should report frame.parent()', async({page, server}) => { + itFailsFirefox('should report frame.parent()', async() => { + const { page, server } = getTestState(); + await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); expect(page.frames()[0].parentFrame()).toBe(null); expect(page.frames()[1].parentFrame()).toBe(page.mainFrame()); expect(page.frames()[2].parentFrame()).toBe(page.mainFrame()); }); - it_fails_ffox('should report different frame instance when frame re-attaches', async({page, server}) => { + itFailsFirefox('should report different frame instance when frame re-attaches', async() => { + const { page, server } = getTestState(); + const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await page.evaluate(() => { window.frame = document.querySelector('#frame1'); @@ -208,4 +237,4 @@ module.exports.addTests = function({testRunner, expect}) { expect(frame1).not.toBe(frame2); }); }); -}; +}); diff --git a/test/headful.spec.js b/test/headful.spec.js index e1434aafcda38..aa2a95fe6e436 100644 --- a/test/headful.spec.js +++ b/test/headful.spec.js @@ -17,37 +17,51 @@ const path = require('path'); const os = require('os'); const fs = require('fs'); -const util = require('util'); -const utils = require('./utils'); -const {waitEvent} = utils; +const {promisify} = require('util'); +const {waitEvent} = require('./utils'); +const expect = require('expect'); +const {getTestState} = require('./mocha-utils'); -const rmAsync = util.promisify(require('rimraf')); -const mkdtempAsync = util.promisify(fs.mkdtemp); +const rmAsync = promisify(require('rimraf')); +const mkdtempAsync = promisify(fs.mkdtemp); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); -module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowserOptions}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +const extensionPath = path.join(__dirname, 'assets', 'simple-extension'); + +describeChromeOnly('headful tests', function() { + + /* These tests fire up an actual browser so let's + * allow a higher timeout + */ + this.timeout(10 * 1000); + + let headfulOptions; + let headlessOptions; + let extensionOptions; + + beforeEach(() => { + const {defaultBrowserOptions} = getTestState(); + headfulOptions = Object.assign({}, defaultBrowserOptions, { + headless: false + }); + headlessOptions = Object.assign({}, defaultBrowserOptions, { + headless: true + }); + + extensionOptions = Object.assign({}, defaultBrowserOptions, { + headless: false, + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + ], + }); - const headfulOptions = Object.assign({}, defaultBrowserOptions, { - headless: false - }); - const headlessOptions = Object.assign({}, defaultBrowserOptions, { - headless: true - }); - const extensionPath = path.join(__dirname, 'assets', 'simple-extension'); - const extensionOptions = Object.assign({}, defaultBrowserOptions, { - headless: false, - args: [ - `--disable-extensions-except=${extensionPath}`, - `--load-extension=${extensionPath}`, - ], }); describe('HEADFUL', function() { it('background_page target type should be available', async() => { + const {puppeteer} = getTestState(); const browserWithExtension = await puppeteer.launch(extensionOptions); const page = await browserWithExtension.newPage(); const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page'); @@ -55,7 +69,8 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse await browserWithExtension.close(); expect(backgroundPageTarget).toBeTruthy(); }); - it('target.page() should return a background_page', async({}) => { + it('target.page() should return a background_page', async function() { + const {puppeteer} = getTestState(); const browserWithExtension = await puppeteer.launch(extensionOptions); const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page'); const page = await backgroundPageTarget.page(); @@ -64,12 +79,15 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse await browserWithExtension.close(); }); it('should have default url when launching browser', async function() { + const {puppeteer} = getTestState(); const browser = await puppeteer.launch(extensionOptions); const pages = (await browser.pages()).map(page => page.url()); expect(pages).toEqual(['about:blank']); await browser.close(); }); - it('headless should be able to read cookies written by headful', async({server}) => { + it('headless should be able to read cookies written by headful', async() => { + const { server, puppeteer } = getTestState(); + const userDataDir = await mkdtempAsync(TMP_FOLDER); // Write a cookie in headful chrome const headfulBrowser = await puppeteer.launch(Object.assign({userDataDir}, headfulOptions)); @@ -88,7 +106,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse expect(cookie).toBe('foo=true'); }); // TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548 - xit('OOPIF: should report google.com frame', async({server}) => { + xit('OOPIF: should report google.com frame', async() => { + const { server } = getTestState(); + // https://google.com is isolated by default in Chromium embedder. const browser = await puppeteer.launch(headfulOptions); const page = await browser.newPage(); @@ -109,7 +129,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse ]); await browser.close(); }); - it('should close browser with beforeunload page', async({server}) => { + it('should close browser with beforeunload page', async() => { + const { server, puppeteer } = getTestState(); + const browser = await puppeteer.launch(headfulOptions); const page = await browser.newPage(); await page.goto(server.PREFIX + '/beforeunload.html'); @@ -118,7 +140,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse await page.click('body'); await browser.close(); }); - it('should open devtools when "devtools: true" option is given', async({server}) => { + it('should open devtools when "devtools: true" option is given', async() => { + const {puppeteer} = getTestState(); + const browser = await puppeteer.launch(Object.assign({devtools: true}, headfulOptions)); const context = await browser.createIncognitoBrowserContext(); await Promise.all([ @@ -131,6 +155,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse describe('Page.bringToFront', function() { it('should work', async() => { + const {puppeteer} = getTestState(); const browser = await puppeteer.launch(headfulOptions); const page1 = await browser.newPage(); const page2 = await browser.newPage(); @@ -148,5 +173,6 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse await browser.close(); }); }); -}; + +}); diff --git a/test/ignorehttpserrors.spec.js b/test/ignorehttpserrors.spec.js index 3ca3c07261432..57796083c462f 100644 --- a/test/ignorehttpserrors.spec.js +++ b/test/ignorehttpserrors.spec.js @@ -14,85 +14,107 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - describe_fails_ffox('ignoreHTTPSErrors', function() { - beforeAll(async state => { - const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions); - state.browser = await puppeteer.launch(options); - }); - afterAll(async state => { - await state.browser.close(); - delete state.browser; - }); - beforeEach(async state => { - state.context = await state.browser.createIncognitoBrowserContext(); - state.page = await state.context.newPage(); - }); - afterEach(async state => { - await state.context.close(); - delete state.context; - delete state.page; - }); +const expect = require('expect'); +const {getTestState} = require('./mocha-utils'); + +describeFailsFirefox('ignoreHTTPSErrors', function() { + /* Note that this test creates its own browser rather than use + * the one provided by the test set-up as we need one + * with ignoreHTTPSErrors set to true + */ + let browser; + let context; + let page; + + before(async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); + const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions); + browser = await puppeteer.launch(options); + }); + + after(async() => { + await browser.close(); + browser = null; + }); + + beforeEach(async() => { + context = await browser.createIncognitoBrowserContext(); + page = await context.newPage(); + }); - describe('Response.securityDetails', function() { - it('should work', async({page, httpsServer}) => { - const [serverRequest, response] = await Promise.all([ - httpsServer.waitForRequest('/empty.html'), - page.goto(httpsServer.EMPTY_PAGE) - ]); - const securityDetails = response.securityDetails(); - expect(securityDetails.issuer()).toBe('puppeteer-tests'); - const protocol = serverRequest.socket.getProtocol().replace('v', ' '); - expect(securityDetails.protocol()).toBe(protocol); - expect(securityDetails.subjectName()).toBe('puppeteer-tests'); - expect(securityDetails.validFrom()).toBe(1550084863); - expect(securityDetails.validTo()).toBe(33086084863); - }); - it('should be |null| for non-secure requests', async({page, server}) => { - const response = await page.goto(server.EMPTY_PAGE); - expect(response.securityDetails()).toBe(null); - }); - it('Network redirects should report SecurityDetails', async({page, httpsServer}) => { - httpsServer.setRedirect('/plzredirect', '/empty.html'); - const responses = []; - page.on('response', response => responses.push(response)); - const [serverRequest, ] = await Promise.all([ - httpsServer.waitForRequest('/plzredirect'), - page.goto(httpsServer.PREFIX + '/plzredirect') - ]); - expect(responses.length).toBe(2); - expect(responses[0].status()).toBe(302); - const securityDetails = responses[0].securityDetails(); - const protocol = serverRequest.socket.getProtocol().replace('v', ' '); - expect(securityDetails.protocol()).toBe(protocol); - }); + afterEach(async() => { + await context.close(); + context = null; + page = null; + }); + + describe('Response.securityDetails', function() { + it('should work', async() => { + const { httpsServer } = getTestState(); + + const [serverRequest, response] = await Promise.all([ + httpsServer.waitForRequest('/empty.html'), + page.goto(httpsServer.EMPTY_PAGE) + ]); + const securityDetails = response.securityDetails(); + expect(securityDetails.issuer()).toBe('puppeteer-tests'); + const protocol = serverRequest.socket.getProtocol().replace('v', ' '); + expect(securityDetails.protocol()).toBe(protocol); + expect(securityDetails.subjectName()).toBe('puppeteer-tests'); + expect(securityDetails.validFrom()).toBe(1550084863); + expect(securityDetails.validTo()).toBe(33086084863); }); + it('should be |null| for non-secure requests', async() => { + const { server } = getTestState(); - it('should work', async({page, httpsServer}) => { - let error = null; - const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); - expect(error).toBe(null); - expect(response.ok()).toBe(true); + const response = await page.goto(server.EMPTY_PAGE); + expect(response.securityDetails()).toBe(null); }); - it('should work with request interception', async({page, server, httpsServer}) => { - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - const response = await page.goto(httpsServer.EMPTY_PAGE); - expect(response.status()).toBe(200); + it('Network redirects should report SecurityDetails', async() => { + const { httpsServer } = getTestState(); + + httpsServer.setRedirect('/plzredirect', '/empty.html'); + const responses = []; + page.on('response', response => responses.push(response)); + const [serverRequest, ] = await Promise.all([ + httpsServer.waitForRequest('/plzredirect'), + page.goto(httpsServer.PREFIX + '/plzredirect') + ]); + expect(responses.length).toBe(2); + expect(responses[0].status()).toBe(302); + const securityDetails = responses[0].securityDetails(); + const protocol = serverRequest.socket.getProtocol().replace('v', ' '); + expect(securityDetails.protocol()).toBe(protocol); }); - it('should work with mixed content', async({page, server, httpsServer}) => { - httpsServer.setRoute('/mixedcontent.html', (req, res) => { - res.end(``); - }); - await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'load'}); - expect(page.frames().length).toBe(2); - // Make sure blocked iframe has functional execution context - // @see https://github.com/puppeteer/puppeteer/issues/2709 - expect(await page.frames()[0].evaluate('1 + 2')).toBe(3); - expect(await page.frames()[1].evaluate('2 + 3')).toBe(5); + }); + + it('should work', async() => { + const { httpsServer } = getTestState(); + + let error = null; + const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); + expect(error).toBe(null); + expect(response.ok()).toBe(true); + }); + it('should work with request interception', async() => { + const { httpsServer } = getTestState(); + + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + const response = await page.goto(httpsServer.EMPTY_PAGE); + expect(response.status()).toBe(200); + }); + it('should work with mixed content', async() => { + const { server, httpsServer } = getTestState(); + + httpsServer.setRoute('/mixedcontent.html', (req, res) => { + res.end(``); }); + await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'load'}); + expect(page.frames().length).toBe(2); + // Make sure blocked iframe has functional execution context + // @see https://github.com/puppeteer/puppeteer/issues/2709 + expect(await page.frames()[0].evaluate('1 + 2')).toBe(3); + expect(await page.frames()[1].evaluate('2 + 3')).toBe(5); }); -}; +}); diff --git a/test/input.spec.js b/test/input.spec.js index 39ef35ab1fa16..4418fa5845163 100644 --- a/test/input.spec.js +++ b/test/input.spec.js @@ -15,15 +15,19 @@ */ const path = require('path'); +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); const FILE_TO_UPLOAD = path.join(__dirname, '/assets/file-to-upload.txt'); -module.exports.addTests = function({testRunner, expect, puppeteer}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - describe_fails_ffox('input', function() { - it('should upload the file', async({page, server}) => { +describe('input tests', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + + describeFailsFirefox('input', function() { + it('should upload the file', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/fileupload.html'); const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD); const input = await page.$('input'); @@ -44,8 +48,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); - describe_fails_ffox('Page.waitForFileChooser', function() { - it('should work when file input is attached to DOM', async({page, server}) => { + describeFailsFirefox('Page.waitForFileChooser', function() { + it('should work when file input is attached to DOM', async() => { + const { page } = getTestState(); + await page.setContent(``); const [chooser] = await Promise.all([ page.waitForFileChooser(), @@ -53,7 +59,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { ]); expect(chooser).toBeTruthy(); }); - it('should work when file input is not attached to DOM', async({page, server}) => { + it('should work when file input is not attached to DOM', async() => { + const { page } = getTestState(); + const [chooser] = await Promise.all([ page.waitForFileChooser(), page.evaluate(() => { @@ -64,24 +72,32 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { ]); expect(chooser).toBeTruthy(); }); - it('should respect timeout', async({page, server}) => { + it('should respect timeout', async() => { + const { page, puppeteer } = getTestState(); + let error = null; await page.waitForFileChooser({timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should respect default timeout when there is no custom timeout', async({page, server}) => { + it('should respect default timeout when there is no custom timeout', async() => { + const { page, puppeteer } = getTestState(); + page.setDefaultTimeout(1); let error = null; await page.waitForFileChooser().catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should prioritize exact timeout over default timeout', async({page, server}) => { + it('should prioritize exact timeout over default timeout', async() => { + const { page, puppeteer } = getTestState(); + page.setDefaultTimeout(0); let error = null; await page.waitForFileChooser({timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should work with no timeout', async({page, server}) => { + it('should work with no timeout', async() => { + const { page } = getTestState(); + const [chooser] = await Promise.all([ page.waitForFileChooser({timeout: 0}), page.evaluate(() => setTimeout(() => { @@ -92,7 +108,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { ]); expect(chooser).toBeTruthy(); }); - it('should return the same file chooser when there are many watchdogs simultaneously', async({page, server}) => { + it('should return the same file chooser when there are many watchdogs simultaneously', async() => { + const { page } = getTestState(); + await page.setContent(``); const [fileChooser1, fileChooser2] = await Promise.all([ page.waitForFileChooser(), @@ -103,8 +121,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); - describe_fails_ffox('FileChooser.accept', function() { - it('should accept single file', async({page, server}) => { + describeFailsFirefox('FileChooser.accept', function() { + it('should accept single file', async() => { + const { page } = getTestState(); + await page.setContent(``); const [chooser] = await Promise.all([ page.waitForFileChooser(), @@ -117,7 +137,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { expect(await page.$eval('input', input => input.files.length)).toBe(1); expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt'); }); - it('should be able to read selected file', async({page, server}) => { + it('should be able to read selected file', async() => { + const { page } = getTestState(); + await page.setContent(``); page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD])); expect(await page.$eval('input', async picker => { @@ -129,7 +151,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { return promise.then(() => reader.result); })).toBe('contents of the file'); }); - it('should be able to reset selected files with empty file list', async({page, server}) => { + it('should be able to reset selected files with empty file list', async() => { + const { page } = getTestState(); + await page.setContent(``); page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD])); expect(await page.$eval('input', async picker => { @@ -144,7 +168,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { return picker.files.length; })).toBe(0); }); - it('should not accept multiple files for single-file input', async({page, server}) => { + it('should not accept multiple files for single-file input', async() => { + const { page } = getTestState(); + await page.setContent(``); const [chooser] = await Promise.all([ page.waitForFileChooser(), @@ -157,7 +183,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { ]).catch(e => error = e); expect(error).not.toBe(null); }); - it('should fail when accepting file chooser twice', async({page, server}) => { + it('should fail when accepting file chooser twice', async() => { + const { page } = getTestState(); + await page.setContent(``); const [fileChooser] = await Promise.all([ page.waitForFileChooser(), @@ -170,8 +198,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); - describe_fails_ffox('FileChooser.cancel', function() { - it('should cancel dialog', async({page, server}) => { + describeFailsFirefox('FileChooser.cancel', function() { + it('should cancel dialog', async() => { + const { page } = getTestState(); + // Consider file chooser canceled if we can summon another one. // There's no reliable way in WebPlatform to see that FileChooser was // canceled. @@ -187,7 +217,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { page.$eval('input', input => input.click()), ]); }); - it('should fail when canceling file chooser twice', async({page, server}) => { + it('should fail when canceling file chooser twice', async() => { + const { page } = getTestState(); + await page.setContent(``); const [fileChooser] = await Promise.all([ page.waitForFileChooser(), @@ -200,8 +232,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { }); }); - describe_fails_ffox('FileChooser.isMultiple', () => { - it('should work for single file pick', async({page, server}) => { + describeFailsFirefox('FileChooser.isMultiple', () => { + it('should work for single file pick', async() => { + const { page } = getTestState(); + await page.setContent(``); const [chooser] = await Promise.all([ page.waitForFileChooser(), @@ -209,7 +243,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { ]); expect(chooser.isMultiple()).toBe(false); }); - it('should work for "multiple"', async({page, server}) => { + it('should work for "multiple"', async() => { + const { page } = getTestState(); + await page.setContent(``); const [chooser] = await Promise.all([ page.waitForFileChooser(), @@ -217,7 +253,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { ]); expect(chooser.isMultiple()).toBe(true); }); - it('should work for "webkitdirectory"', async({page, server}) => { + it('should work for "webkitdirectory"', async() => { + const { page } = getTestState(); + await page.setContent(``); const [chooser] = await Promise.all([ page.waitForFileChooser(), @@ -226,4 +264,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) { expect(chooser.isMultiple()).toBe(true); }); }); -}; +}); diff --git a/test/jshandle.spec.js b/test/jshandle.spec.js index ac13b5d8fc42d..5f11172a56f5c 100644 --- a/test/jshandle.spec.js +++ b/test/jshandle.spec.js @@ -14,27 +14,37 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, CHROME}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); + +describe('JSHandle', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Page.evaluateHandle', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page } = getTestState(); + const windowHandle = await page.evaluateHandle(() => window); expect(windowHandle).toBeTruthy(); }); - it('should accept object handle as an argument', async({page, server}) => { + it('should accept object handle as an argument', async() => { + const { page } = getTestState(); + const navigatorHandle = await page.evaluateHandle(() => navigator); const text = await page.evaluate(e => e.userAgent, navigatorHandle); expect(text).toContain('Mozilla'); }); - it('should accept object handle to primitive types', async({page, server}) => { + it('should accept object handle to primitive types', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => 5); const isFive = await page.evaluate(e => Object.is(e, 5), aHandle); expect(isFive).toBeTruthy(); }); - it('should warn on nested object handles', async({page, server}) => { + it('should warn on nested object handles', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => document.body); let error = null; await page.evaluateHandle( @@ -43,18 +53,24 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { ).catch(e => error = e); expect(error.message).toContain('Are you passing a nested JSHandle?'); }); - it('should accept object handle to unserializable value', async({page, server}) => { + it('should accept object handle to unserializable value', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => Infinity); expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true); }); - it('should use the same JS wrappers', async({page, server}) => { + it('should use the same JS wrappers', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => { window.FOO = 123; return window; }); expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123); }); - it('should work with primitives', async({page, server}) => { + it('should work with primitives', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => { window.FOO = 123; return window; @@ -64,7 +80,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('JSHandle.getProperty', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => ({ one: 1, two: 2, @@ -76,21 +94,27 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('JSHandle.jsonValue', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => ({foo: 'bar'})); const json = await aHandle.jsonValue(); expect(json).toEqual({foo: 'bar'}); }); - it_fails_ffox('should not work with dates', async({page, server}) => { + itFailsFirefox('should not work with dates', async() => { + const { page } = getTestState(); + const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z')); const json = await dateHandle.jsonValue(); expect(json).toEqual({}); }); - it('should throw for circular objects', async({page, server}) => { + it('should throw for circular objects', async() => { + const { page, isChrome } = getTestState(); + const windowHandle = await page.evaluateHandle('window'); let error = null; await windowHandle.jsonValue().catch(e => error = e); - if (CHROME) + if (isChrome) expect(error.message).toContain('Object reference chain is too long'); else expect(error.message).toContain('Object is not serializable'); @@ -98,7 +122,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('JSHandle.getProperties', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => ({ foo: 'bar' })); @@ -107,7 +133,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(foo).toBeTruthy(); expect(await foo.jsonValue()).toBe('bar'); }); - it('should return even non-own properties', async({page, server}) => { + it('should return even non-own properties', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => { class A { constructor() { @@ -129,24 +157,32 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('JSHandle.asElement', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => document.body); const element = aHandle.asElement(); expect(element).toBeTruthy(); }); - it('should return null for non-elements', async({page, server}) => { + it('should return null for non-elements', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => 2); const element = aHandle.asElement(); expect(element).toBeFalsy(); }); - it_fails_ffox('should return ElementHandle for TextNodes', async({page, server}) => { + itFailsFirefox('should return ElementHandle for TextNodes', async() => { + const { page } = getTestState(); + await page.setContent('
ee!
'); const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild); const element = aHandle.asElement(); expect(element).toBeTruthy(); expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element)); }); - it_fails_ffox('should work with nullified Node', async({page, server}) => { + itFailsFirefox('should work with nullified Node', async() => { + const { page } = getTestState(); + await page.setContent('
test
'); await page.evaluate(() => delete Node); const handle = await page.evaluateHandle(() => document.querySelector('section')); @@ -156,17 +192,23 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('JSHandle.toString', function() { - it('should work for primitives', async({page, server}) => { + it('should work for primitives', async() => { + const { page } = getTestState(); + const numberHandle = await page.evaluateHandle(() => 2); expect(numberHandle.toString()).toBe('JSHandle:2'); const stringHandle = await page.evaluateHandle(() => 'a'); expect(stringHandle.toString()).toBe('JSHandle:a'); }); - it('should work for complicated objects', async({page, server}) => { + it('should work for complicated objects', async() => { + const { page } = getTestState(); + const aHandle = await page.evaluateHandle(() => window); expect(aHandle.toString()).toBe('JSHandle@object'); }); - it('should work with different subtypes', async({page, server}) => { + it('should work with different subtypes', async() => { + const { page } = getTestState(); + expect((await page.evaluateHandle('(function(){})')).toString()).toBe('JSHandle@function'); expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle:12'); expect((await page.evaluateHandle('true')).toString()).toBe('JSHandle:true'); @@ -187,4 +229,4 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy'); }); }); -}; +}); diff --git a/test/keyboard.spec.js b/test/keyboard.spec.js index 1347514b04d3c..ffe0232890cb7 100644 --- a/test/keyboard.spec.js +++ b/test/keyboard.spec.js @@ -16,239 +16,274 @@ const utils = require('./utils'); const os = require('os'); +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect, FFOX}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('Keyboard', function() { - it('should type into a textarea', async({page, server}) => { - await page.evaluate(() => { - const textarea = document.createElement('textarea'); - document.body.appendChild(textarea); - textarea.focus(); - }); - const text = 'Hello world. I am the text that was typed!'; - await page.keyboard.type(text); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text); - }); - it_fails_ffox('should press the metaKey', async({page}) => { - await page.evaluate(() => { - window.keyPromise = new Promise(resolve => document.addEventListener('keydown', event => resolve(event.key))); - }); - await page.keyboard.press('Meta'); - expect(await page.evaluate('keyPromise')).toBe(FFOX && os.platform() !== 'darwin' ? 'OS' : 'Meta'); - }); - it('should move with the arrow keys', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.type('textarea', 'Hello World!'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!'); - for (let i = 0; i < 'World!'.length; i++) - page.keyboard.press('ArrowLeft'); - await page.keyboard.type('inserted '); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!'); - page.keyboard.down('Shift'); - for (let i = 0; i < 'inserted '.length; i++) - page.keyboard.press('ArrowLeft'); - page.keyboard.up('Shift'); - await page.keyboard.press('Backspace'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!'); - }); - it('should send a character with ElementHandle.press', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = await page.$('textarea'); - await textarea.press('a'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a'); +describe('Keyboard', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); - await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); + it('should type into a textarea', async() => { + const { page } = getTestState(); - await textarea.press('b'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a'); - }); - it_fails_ffox('ElementHandle.press should support |text| option', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - const textarea = await page.$('textarea'); - await textarea.press('a', {text: 'ё'}); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('ё'); - }); - it_fails_ffox('should send a character with sendCharacter', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - await page.keyboard.sendCharacter('嗨'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨'); - await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); - await page.keyboard.sendCharacter('a'); - expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a'); - }); - it_fails_ffox('should report shiftKey', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - const keyboard = page.keyboard; - const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17}; - for (const modifierKey in codeForKey) { - await keyboard.down(modifierKey); - expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']'); - await keyboard.down('!'); - // Shift+! will generate a keypress - if (modifierKey === 'Shift') - expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']'); - else - expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']'); - - await keyboard.up('!'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']'); - await keyboard.up(modifierKey); - expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []'); - } + await page.evaluate(() => { + const textarea = document.createElement('textarea'); + document.body.appendChild(textarea); + textarea.focus(); }); - it('should report multiple modifiers', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - const keyboard = page.keyboard; - await keyboard.down('Control'); - expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]'); - await keyboard.down('Alt'); - expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]'); - await keyboard.down(';'); - expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]'); - await keyboard.up(';'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]'); - await keyboard.up('Control'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]'); - await keyboard.up('Alt'); - expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []'); - }); - it('should send proper codes while typing', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - await page.keyboard.type('!'); - expect(await page.evaluate(() => getResult())).toBe( - [ 'Keydown: ! Digit1 49 []', - 'Keypress: ! Digit1 33 33 []', - 'Keyup: ! Digit1 49 []'].join('\n')); - await page.keyboard.type('^'); - expect(await page.evaluate(() => getResult())).toBe( - [ 'Keydown: ^ Digit6 54 []', - 'Keypress: ^ Digit6 94 94 []', - 'Keyup: ^ Digit6 54 []'].join('\n')); - }); - it('should send proper codes while typing with shift', async({page, server}) => { - await page.goto(server.PREFIX + '/input/keyboard.html'); - const keyboard = page.keyboard; - await keyboard.down('Shift'); - await page.keyboard.type('~'); - expect(await page.evaluate(() => getResult())).toBe( - [ 'Keydown: Shift ShiftLeft 16 [Shift]', - 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode - 'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode - 'Keyup: ~ Backquote 192 [Shift]'].join('\n')); - await keyboard.up('Shift'); - }); - it('should not type canceled events', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - await page.evaluate(() => { - window.addEventListener('keydown', event => { - event.stopPropagation(); - event.stopImmediatePropagation(); - if (event.key === 'l') - event.preventDefault(); - if (event.key === 'o') - event.preventDefault(); - }, false); - }); - await page.keyboard.type('Hello World!'); - expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!'); - }); - it_fails_ffox('should specify repeat property', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true)); - await page.keyboard.down('a'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); - await page.keyboard.press('a'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); - - await page.keyboard.down('b'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); - await page.keyboard.down('b'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); - - await page.keyboard.up('a'); - await page.keyboard.down('a'); - expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); - }); - it_fails_ffox('should type all kinds of characters', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - const text = 'This text goes onto two lines.\nThis character is 嗨.'; - await page.keyboard.type(text); - expect(await page.evaluate('result')).toBe(text); + const text = 'Hello world. I am the text that was typed!'; + await page.keyboard.type(text); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text); + }); + itFailsFirefox('should press the metaKey', async() => { + const { page, isFirefox } = getTestState(); + + await page.evaluate(() => { + window.keyPromise = new Promise(resolve => document.addEventListener('keydown', event => resolve(event.key))); }); - it_fails_ffox('should specify location', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.evaluate(() => { - window.addEventListener('keydown', event => window.keyLocation = event.location, true); - }); - const textarea = await page.$('textarea'); + await page.keyboard.press('Meta'); + expect(await page.evaluate('keyPromise')).toBe(isFirefox && os.platform() !== 'darwin' ? 'OS' : 'Meta'); + }); + it('should move with the arrow keys', async() => { + const { page, server } = getTestState(); - await textarea.press('Digit5'); - expect(await page.evaluate('keyLocation')).toBe(0); + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.type('textarea', 'Hello World!'); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!'); + for (let i = 0; i < 'World!'.length; i++) + page.keyboard.press('ArrowLeft'); + await page.keyboard.type('inserted '); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!'); + page.keyboard.down('Shift'); + for (let i = 0; i < 'inserted '.length; i++) + page.keyboard.press('ArrowLeft'); + page.keyboard.up('Shift'); + await page.keyboard.press('Backspace'); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!'); + }); + it('should send a character with ElementHandle.press', async() => { + const { page, server } = getTestState(); - await textarea.press('ControlLeft'); - expect(await page.evaluate('keyLocation')).toBe(1); + await page.goto(server.PREFIX + '/input/textarea.html'); + const textarea = await page.$('textarea'); + await textarea.press('a'); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a'); - await textarea.press('ControlRight'); - expect(await page.evaluate('keyLocation')).toBe(2); + await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); - await textarea.press('NumpadSubtract'); - expect(await page.evaluate('keyLocation')).toBe(3); - }); - it('should throw on unknown keys', async({page, server}) => { - let error = await page.keyboard.press('NotARealKey').catch(e => e); - expect(error.message).toBe('Unknown key: "NotARealKey"'); + await textarea.press('b'); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a'); + }); + itFailsFirefox('ElementHandle.press should support |text| option', async() => { + const { page, server } = getTestState(); - error = await page.keyboard.press('ё').catch(e => e); - expect(error && error.message).toBe('Unknown key: "ё"'); + await page.goto(server.PREFIX + '/input/textarea.html'); + const textarea = await page.$('textarea'); + await textarea.press('a', {text: 'ё'}); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('ё'); + }); + itFailsFirefox('should send a character with sendCharacter', async() => { + const { page, server } = getTestState(); - error = await page.keyboard.press('😊').catch(e => e); - expect(error && error.message).toBe('Unknown key: "😊"'); - }); - it_fails_ffox('should type emoji', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.type('textarea', '👹 Tokyo street Japan 🇯🇵'); - expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + await page.keyboard.sendCharacter('嗨'); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨'); + await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); + await page.keyboard.sendCharacter('a'); + expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a'); + }); + itFailsFirefox('should report shiftKey', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/keyboard.html'); + const keyboard = page.keyboard; + const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17}; + for (const modifierKey in codeForKey) { + await keyboard.down(modifierKey); + expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']'); + await keyboard.down('!'); + // Shift+! will generate a keypress + if (modifierKey === 'Shift') + expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']'); + else + expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']'); + + await keyboard.up('!'); + expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']'); + await keyboard.up(modifierKey); + expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []'); + } + }); + it('should report multiple modifiers', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/keyboard.html'); + const keyboard = page.keyboard; + await keyboard.down('Control'); + expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]'); + await keyboard.down('Alt'); + expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]'); + await keyboard.down(';'); + expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]'); + await keyboard.up(';'); + expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]'); + await keyboard.up('Control'); + expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]'); + await keyboard.up('Alt'); + expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []'); + }); + it('should send proper codes while typing', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/keyboard.html'); + await page.keyboard.type('!'); + expect(await page.evaluate(() => getResult())).toBe( + [ 'Keydown: ! Digit1 49 []', + 'Keypress: ! Digit1 33 33 []', + 'Keyup: ! Digit1 49 []'].join('\n')); + await page.keyboard.type('^'); + expect(await page.evaluate(() => getResult())).toBe( + [ 'Keydown: ^ Digit6 54 []', + 'Keypress: ^ Digit6 94 94 []', + 'Keyup: ^ Digit6 54 []'].join('\n')); + }); + it('should send proper codes while typing with shift', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/keyboard.html'); + const keyboard = page.keyboard; + await keyboard.down('Shift'); + await page.keyboard.type('~'); + expect(await page.evaluate(() => getResult())).toBe( + [ 'Keydown: Shift ShiftLeft 16 [Shift]', + 'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode + 'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode + 'Keyup: ~ Backquote 192 [Shift]'].join('\n')); + await keyboard.up('Shift'); + }); + it('should not type canceled events', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + await page.evaluate(() => { + window.addEventListener('keydown', event => { + event.stopPropagation(); + event.stopImmediatePropagation(); + if (event.key === 'l') + event.preventDefault(); + if (event.key === 'o') + event.preventDefault(); + }, false); }); - it_fails_ffox('should type emoji into an iframe', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html'); - const frame = page.frames()[1]; - const textarea = await frame.$('textarea'); - await textarea.type('👹 Tokyo street Japan 🇯🇵'); - expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); + await page.keyboard.type('Hello World!'); + expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!'); + }); + itFailsFirefox('should specify repeat property', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true)); + await page.keyboard.down('a'); + expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); + await page.keyboard.press('a'); + expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); + + await page.keyboard.down('b'); + expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); + await page.keyboard.down('b'); + expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true); + + await page.keyboard.up('a'); + await page.keyboard.down('a'); + expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false); + }); + itFailsFirefox('should type all kinds of characters', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + const text = 'This text goes onto two lines.\nThis character is 嗨.'; + await page.keyboard.type(text); + expect(await page.evaluate('result')).toBe(text); + }); + itFailsFirefox('should specify location', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.evaluate(() => { + window.addEventListener('keydown', event => window.keyLocation = event.location, true); }); - it_fails_ffox('should press the meta key', async({page}) => { - await page.evaluate(() => { - window.result = null; - document.addEventListener('keydown', event => { - window.result = [event.key, event.code, event.metaKey]; - }); - }); - await page.keyboard.press('Meta'); - const [key, code, metaKey] = await page.evaluate('result'); - if (FFOX && os.platform() !== 'darwin') - expect(key).toBe('OS'); - else - expect(key).toBe('Meta'); + const textarea = await page.$('textarea'); - if (FFOX) - expect(code).toBe('OSLeft'); - else - expect(code).toBe('MetaLeft'); + await textarea.press('Digit5'); + expect(await page.evaluate('keyLocation')).toBe(0); - if (FFOX && os.platform() !== 'darwin') - expect(metaKey).toBe(false); - else - expect(metaKey).toBe(true); + await textarea.press('ControlLeft'); + expect(await page.evaluate('keyLocation')).toBe(1); + await textarea.press('ControlRight'); + expect(await page.evaluate('keyLocation')).toBe(2); + + await textarea.press('NumpadSubtract'); + expect(await page.evaluate('keyLocation')).toBe(3); + }); + it('should throw on unknown keys', async() => { + const { page } = getTestState(); + + let error = await page.keyboard.press('NotARealKey').catch(e => e); + expect(error.message).toBe('Unknown key: "NotARealKey"'); + + error = await page.keyboard.press('ё').catch(e => e); + expect(error && error.message).toBe('Unknown key: "ё"'); + + error = await page.keyboard.press('😊').catch(e => e); + expect(error && error.message).toBe('Unknown key: "😊"'); + }); + itFailsFirefox('should type emoji', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.type('textarea', '👹 Tokyo street Japan 🇯🇵'); + expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); + }); + itFailsFirefox('should type emoji into an iframe', async() => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html'); + const frame = page.frames()[1]; + const textarea = await frame.$('textarea'); + await textarea.type('👹 Tokyo street Japan 🇯🇵'); + expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵'); + }); + itFailsFirefox('should press the meta key', async() => { + const { page, isFirefox } = getTestState(); + + await page.evaluate(() => { + window.result = null; + document.addEventListener('keydown', event => { + window.result = [event.key, event.code, event.metaKey]; + }); }); + await page.keyboard.press('Meta'); + const [key, code, metaKey] = await page.evaluate('result'); + if (isFirefox && os.platform() !== 'darwin') + expect(key).toBe('OS'); + else + expect(key).toBe('Meta'); + + if (isFirefox) + expect(code).toBe('OSLeft'); + else + expect(code).toBe('MetaLeft'); + + if (isFirefox && os.platform() !== 'darwin') + expect(metaKey).toBe(false); + else + expect(metaKey).toBe(true); + }); -}; +}); diff --git a/test/launcher.spec.js b/test/launcher.spec.js index 4642e67744ba6..1a32fe182cd93 100644 --- a/test/launcher.spec.js +++ b/test/launcher.spec.js @@ -23,15 +23,15 @@ const readFileAsync = helper.promisify(fs.readFile); const statAsync = helper.promisify(fs.stat); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); const utils = require('./utils'); +const expect = require('expect'); +const {getTestState} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, CHROME, FFOX, puppeteerPath}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - +describe('Launcher specs', function() { describe('Puppeteer', function() { describe('BrowserFetcher', function() { - it('should download and extract chrome linux binary', async({server}) => { + it('should download and extract chrome linux binary', async() => { + const { server, puppeteer} = getTestState(); + const downloadsFolder = await mkdtempAsync(TMP_FOLDER); const browserFetcher = puppeteer.createBrowserFetcher({ platform: 'linux', @@ -61,9 +61,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(await browserFetcher.localRevisions()).toEqual([]); await rmAsync(downloadsFolder); }); - }); - describe('BrowserFetcher', function() { - it('should download and extract firefox linux binary', async({server}) => { + it('should download and extract firefox linux binary', async() => { + const { server , puppeteer} = getTestState(); + const downloadsFolder = await mkdtempAsync(TMP_FOLDER); const browserFetcher = puppeteer.createBrowserFetcher({ platform: 'linux', @@ -94,8 +94,10 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await rmAsync(downloadsFolder); }); }); + describe('Browser.disconnect', function() { - it('should reject navigation when browser closes', async({server}) => { + it('should reject navigation when browser closes', async() => { + const { server, puppeteer, defaultBrowserOptions} = getTestState(); server.setRoute('/one-style.css', () => {}); const browser = await puppeteer.launch(defaultBrowserOptions); const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()}); @@ -107,7 +109,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(error.message).toBe('Navigation failed because browser has disconnected!'); await browser.close(); }); - it('should reject waitForSelector when browser closes', async({server}) => { + it('should reject waitForSelector when browser closes', async() => { + const { server, puppeteer, defaultBrowserOptions} = getTestState(); + server.setRoute('/empty.html', () => {}); const browser = await puppeteer.launch(defaultBrowserOptions); const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()}); @@ -120,7 +124,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p }); }); describe('Browser.close', function() { - it('should terminate network waiters', async({context, server}) => { + it('should terminate network waiters', async() => { + const { server, puppeteer, defaultBrowserOptions } = getTestState(); + const browser = await puppeteer.launch(defaultBrowserOptions); const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()}); const newPage = await remote.newPage(); @@ -134,10 +140,12 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(message).toContain('Target closed'); expect(message).not.toContain('Timeout'); } + await browser.close(); }); }); describe('Puppeteer.launch', function() { it('should reject all promises when browser is closed', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); const browser = await puppeteer.launch(defaultBrowserOptions); const page = await browser.newPage(); let error = null; @@ -146,13 +154,17 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await neverResolves; expect(error.message).toContain('Protocol error'); }); - it('should reject if executable path is invalid', async({server}) => { + it('should reject if executable path is invalid', async() => { + const { defaultBrowserOptions, puppeteer} = getTestState(); + let waitError = null; const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'}); await puppeteer.launch(options).catch(e => waitError = e); expect(waitError.message).toContain('Failed to launch'); }); - it('userDataDir option', async({server}) => { + it('userDataDir option', async() => { + const { defaultBrowserOptions, puppeteer} = getTestState(); + const userDataDir = await mkdtempAsync(TMP_FOLDER); const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); @@ -164,10 +176,12 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 await rmAsync(userDataDir).catch(e => {}); }); - it('userDataDir argument', async({server}) => { + it('userDataDir argument', async() => { + const { isChrome, puppeteer, defaultBrowserOptions} = getTestState(); + const userDataDir = await mkdtempAsync(TMP_FOLDER); const options = Object.assign({}, defaultBrowserOptions); - if (CHROME) { + if (isChrome) { options.args = [ ...(defaultBrowserOptions.args || []), `--user-data-dir=${userDataDir}` @@ -186,7 +200,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778 await rmAsync(userDataDir).catch(e => {}); }); - it('userDataDir option should restore state', async({server}) => { + it('userDataDir option should restore state', async() => { + const { server, puppeteer, defaultBrowserOptions} = getTestState(); + const userDataDir = await mkdtempAsync(TMP_FOLDER); const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); @@ -204,7 +220,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await rmAsync(userDataDir).catch(e => {}); }); // This mysteriously fails on Windows on AppVeyor. See https://github.com/puppeteer/puppeteer/issues/4111 - xit('userDataDir option should restore cookies', async({server}) => { + xit('userDataDir option should restore cookies', async() => { + const { server , puppeteer} = getTestState(); + const userDataDir = await mkdtempAsync(TMP_FOLDER); const options = Object.assign({userDataDir}, defaultBrowserOptions); const browser = await puppeteer.launch(options); @@ -222,12 +240,14 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await rmAsync(userDataDir).catch(e => {}); }); it('should return the default arguments', async() => { - if (CHROME) { + const {isChrome, isFirefox, puppeteer} = getTestState(); + + if (isChrome) { expect(puppeteer.defaultArgs()).toContain('--no-first-run'); expect(puppeteer.defaultArgs()).toContain('--headless'); expect(puppeteer.defaultArgs({headless: false})).not.toContain('--headless'); expect(puppeteer.defaultArgs({userDataDir: 'foo'})).toContain('--user-data-dir=foo'); - } else if (FFOX) { + } else if (isFirefox) { expect(puppeteer.defaultArgs()).toContain('--headless'); expect(puppeteer.defaultArgs()).toContain('--no-remote'); expect(puppeteer.defaultArgs()).toContain('--foreground'); @@ -242,12 +262,14 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p } }); it('should report the correct product', async() => { - if (CHROME) + const {isChrome, isFirefox, puppeteer} = getTestState(); + if (isChrome) expect(puppeteer.product).toBe('chrome'); - else if (FFOX) + else if (isFirefox) expect(puppeteer.product).toBe('firefox'); }); - it_fails_ffox('should work with no default arguments', async() => { + itFailsFirefox('should work with no default arguments', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); const options = Object.assign({}, defaultBrowserOptions); options.ignoreDefaultArgs = true; const browser = await puppeteer.launch(options); @@ -256,7 +278,8 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await page.close(); await browser.close(); }); - it_fails_ffox('should filter out ignored default arguments', async() => { + itFailsFirefox('should filter out ignored default arguments', async() => { + const {defaultBrowserOptions, puppeteer} = getTestState(); // Make sure we launch with `--enable-automation` by default. const defaultArgs = puppeteer.defaultArgs(); const browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { @@ -270,12 +293,15 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await browser.close(); }); it('should have default URL when launching browser', async function() { + const {defaultBrowserOptions, puppeteer} = getTestState(); const browser = await puppeteer.launch(defaultBrowserOptions); const pages = (await browser.pages()).map(page => page.url()); expect(pages).toEqual(['about:blank']); await browser.close(); }); - it_fails_ffox('should have custom URL when launching browser', async function({server}) { + itFailsFirefox('should have custom URL when launching browser', async() => { + const { server, puppeteer, defaultBrowserOptions} = getTestState(); + const options = Object.assign({}, defaultBrowserOptions); options.args = [server.EMPTY_PAGE].concat(options.args || []); const browser = await puppeteer.launch(options); @@ -288,6 +314,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await browser.close(); }); it('should set the default viewport', async() => { + const {puppeteer, defaultBrowserOptions} = getTestState(); const options = Object.assign({}, defaultBrowserOptions, { defaultViewport: { width: 456, @@ -301,6 +328,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await browser.close(); }); it('should disable the default viewport', async() => { + const {puppeteer, defaultBrowserOptions} = getTestState(); const options = Object.assign({}, defaultBrowserOptions, { defaultViewport: null }); @@ -309,7 +337,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(page.viewport()).toBe(null); await browser.close(); }); - it_fails_ffox('should take fullPage screenshots when defaultViewport is null', async({server}) => { + itFailsFirefox('should take fullPage screenshots when defaultViewport is null', async() => { + const { server, puppeteer, defaultBrowserOptions} = getTestState(); + const options = Object.assign({}, defaultBrowserOptions, { defaultViewport: null }); @@ -324,20 +354,24 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p }); }); describe('Puppeteer.connect', function() { - it('should be able to connect multiple times to the same browser', async({server}) => { + it('should be able to connect multiple times to the same browser', async() => { + const { puppeteer, defaultBrowserOptions} = getTestState(); + const originalBrowser = await puppeteer.launch(defaultBrowserOptions); - const browser = await puppeteer.connect({ + const otherBrowser = await puppeteer.connect({ browserWSEndpoint: originalBrowser.wsEndpoint() }); - const page = await browser.newPage(); + const page = await otherBrowser.newPage(); expect(await page.evaluate(() => 7 * 8)).toBe(56); - browser.disconnect(); + otherBrowser.disconnect(); const secondPage = await originalBrowser.newPage(); expect(await secondPage.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work'); await originalBrowser.close(); }); - it('should be able to close remote browser', async({server}) => { + it('should be able to close remote browser', async() => { + const { defaultBrowserOptions, puppeteer} = getTestState(); + const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const remoteBrowser = await puppeteer.connect({ browserWSEndpoint: originalBrowser.wsEndpoint() @@ -347,7 +381,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p remoteBrowser.close(), ]); }); - it_fails_ffox('should support ignoreHTTPSErrors option', async({httpsServer}) => { + itFailsFirefox('should support ignoreHTTPSErrors option', async() => { + const { httpsServer, puppeteer, defaultBrowserOptions} = getTestState(); + const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const browserWSEndpoint = originalBrowser.wsEndpoint(); @@ -366,7 +402,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await page.close(); await browser.close(); }); - it_fails_ffox('should be able to reconnect to a disconnected browser', async({server}) => { + itFailsFirefox('should be able to reconnect to a disconnected browser', async() => { + const { server , puppeteer, defaultBrowserOptions} = getTestState(); + const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const browserWSEndpoint = originalBrowser.wsEndpoint(); const page = await originalBrowser.newPage(); @@ -387,7 +425,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p await browser.close(); }); // @see https://github.com/puppeteer/puppeteer/issues/4197#issuecomment-481793410 - it_fails_ffox('should be able to connect to the same page simultaneously', async({server}) => { + itFailsFirefox('should be able to connect to the same page simultaneously', async() => { + const { puppeteer } = getTestState(); + const browserOne = await puppeteer.launch(); const browserTwo = await puppeteer.connect({ browserWSEndpoint: browserOne.wsEndpoint() }); const [page1, page2] = await Promise.all([ @@ -401,7 +441,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p }); describe('Puppeteer.executablePath', function() { - it_fails_ffox('should work', async({server}) => { + itFailsFirefox('should work', async() => { + const { puppeteer } = getTestState(); + const executablePath = puppeteer.executablePath(); expect(fs.existsSync(executablePath)).toBe(true); expect(fs.realpathSync(executablePath)).toBe(executablePath); @@ -411,17 +453,21 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p describe('Top-level requires', function() { it('should require top-level Errors', async() => { + const {puppeteer, puppeteerPath} = getTestState(); const Errors = require(path.join(puppeteerPath, '/Errors')); expect(Errors.TimeoutError).toBe(puppeteer.errors.TimeoutError); }); it('should require top-level DeviceDescriptors', async() => { + const {puppeteer, puppeteerPath} = getTestState(); const Devices = require(path.join(puppeteerPath, '/DeviceDescriptors')); expect(Devices['iPhone 6']).toBe(puppeteer.devices['iPhone 6']); }); }); describe('Browser target events', function() { - it_fails_ffox('should work', async({server}) => { + itFailsFirefox('should work', async() => { + const { server , puppeteer, defaultBrowserOptions} = getTestState(); + const browser = await puppeteer.launch(defaultBrowserOptions); const events = []; browser.on('targetcreated', () => events.push('CREATED')); @@ -437,6 +483,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p describe('Browser.Events.disconnected', function() { it('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async() => { + const {puppeteer, defaultBrowserOptions} = getTestState(); const originalBrowser = await puppeteer.launch(defaultBrowserOptions); const browserWSEndpoint = originalBrowser.wsEndpoint(); const remoteBrowser1 = await puppeteer.connect({browserWSEndpoint}); @@ -469,5 +516,4 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p expect(disconnectedRemote2).toBe(1); }); }); - -}; +}); diff --git a/test/mocha-utils.js b/test/mocha-utils.js new file mode 100644 index 0000000000000..6e9dadbb3845f --- /dev/null +++ b/test/mocha-utils.js @@ -0,0 +1,155 @@ +/** + * Copyright 2020 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const {TestServer} = require('../utils/testserver/index'); +const path = require('path'); +const fs = require('fs'); +const puppeteer = require('../'); +const utils = require('./utils'); +const assertCoverage = require('./coverage-utils'); + +const setupServer = async() => { + const assetsPath = path.join(__dirname, 'assets'); + const cachedPath = path.join(__dirname, 'assets', 'cached'); + + const port = 8907; + const server = await TestServer.create(assetsPath, port); + server.enableHTTPCache(cachedPath); + server.PORT = port; + server.PREFIX = `http://localhost:${port}`; + server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`; + server.EMPTY_PAGE = `http://localhost:${port}/empty.html`; + + const httpsPort = port + 1; + const httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort); + httpsServer.enableHTTPCache(cachedPath); + httpsServer.PORT = httpsPort; + httpsServer.PREFIX = `https://localhost:${httpsPort}`; + httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`; + httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`; + + return {server, httpsServer}; +}; + +exports.getTestState = () => state; + +const product = process.env.PRODUCT || process.env.PUPPETEER_PRODUCT || 'Chromium'; + +const isHeadless = (process.env.HEADLESS || 'true').trim().toLowerCase() === 'true'; +const isFirefox = product === 'firefox'; +const isChrome = product === 'Chromium'; +const defaultBrowserOptions = { + handleSIGINT: false, + executablePath: process.env.BINARY, + slowMo: false, + headless: isHeadless, + dumpio: !!process.env.DUMPIO, +}; + + +if (defaultBrowserOptions.executablePath) { + console.warn(`WARN: running ${product} tests with ${defaultBrowserOptions.executablePath}`); +} else { + const executablePath = puppeteer.executablePath(); + if (!fs.existsSync(executablePath)) + throw new Error(`Browser is not downloaded at ${executablePath}. Run 'npm install' and try to re-run tests`); +} + +const setupGoldenAssertions = () => { + const suffix = product.toLowerCase(); + const GOLDEN_DIR = path.join(__dirname, 'golden-' + suffix); + const OUTPUT_DIR = path.join(__dirname, 'output-' + suffix); + if (fs.existsSync(OUTPUT_DIR)) + rm(OUTPUT_DIR); + utils.extendExpectWithToBeGolden(GOLDEN_DIR, OUTPUT_DIR); +}; + +setupGoldenAssertions(); + +const state = {}; + +global.itFailsFirefox = (...args) => { + if (isFirefox) + return xit(...args); + else + return it(...args); +}; + +global.describeFailsFirefox = (...args) => { + if (isFirefox) + return xdescribe(...args); + else + return describe(...args); +}; + +global.describeChromeOnly = (...args) => { + if (isChrome) + return describe(...args); +}; + +if (process.env.COVERAGE) + assertCoverage(); + +exports.setupTestBrowserHooks = () => { + before(async() => { + const browser = await puppeteer.launch(defaultBrowserOptions); + state.browser = browser; + }); + + after(async() => { + await state.browser.close(); + state.browser = null; + }); +}; + +exports.setupTestPageAndContextHooks = () => { + beforeEach(async() => { + state.context = await state.browser.createIncognitoBrowserContext(); + state.page = await state.context.newPage(); + }); + + afterEach(async() => { + await state.context.close(); + state.context = null; + state.page = null; + }); +}; + + +before(async() => { + const {server, httpsServer} = await setupServer(); + + state.puppeteer = puppeteer; + state.defaultBrowserOptions = defaultBrowserOptions; + state.server = server; + state.httpsServer = httpsServer; + state.isFirefox = isFirefox; + state.isChrome = isChrome; + state.isHeadless = isHeadless; + state.puppeteerPath = path.resolve(path.join(__dirname, '..')); +}); + +beforeEach(async() => { + state.server.reset(); + state.httpsServer.reset(); +}); + +after(async() => { + await state.server.stop(); + state.server = null; + await state.httpsServer.stop(); + state.httpsServer = null; +}); diff --git a/test/mouse.spec.js b/test/mouse.spec.js index 57d8fd30e9a3d..21815838f7f58 100644 --- a/test/mouse.spec.js +++ b/test/mouse.spec.js @@ -14,6 +14,8 @@ * limitations under the License. */ const os = require('os'); +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); function dimensions() { const rect = document.querySelector('textarea').getBoundingClientRect(); @@ -25,132 +27,144 @@ function dimensions() { }; } -module.exports.addTests = function({testRunner, expect, FFOX}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('Mouse', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + it('should click the document', async() => { + const { page } = getTestState(); - describe('Mouse', function() { - it('should click the document', async({page, server}) => { - await page.evaluate(() => { - window.clickPromise = new Promise(resolve => { - document.addEventListener('click', event => { - resolve({ - type: event.type, - detail: event.detail, - clientX: event.clientX, - clientY: event.clientY, - isTrusted: event.isTrusted, - button: event.button - }); + await page.evaluate(() => { + window.clickPromise = new Promise(resolve => { + document.addEventListener('click', event => { + resolve({ + type: event.type, + detail: event.detail, + clientX: event.clientX, + clientY: event.clientY, + isTrusted: event.isTrusted, + button: event.button }); }); }); - await page.mouse.click(50, 60); - const event = await page.evaluate(() => window.clickPromise); - expect(event.type).toBe('click'); - expect(event.detail).toBe(1); - expect(event.clientX).toBe(50); - expect(event.clientY).toBe(60); - expect(event.isTrusted).toBe(true); - expect(event.button).toBe(0); - }); - it_fails_ffox('should resize the textarea', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - const {x, y, width, height} = await page.evaluate(dimensions); - const mouse = page.mouse; - await mouse.move(x + width - 4, y + height - 4); - await mouse.down(); - await mouse.move(x + width + 100, y + height + 100); - await mouse.up(); - const newDimensions = await page.evaluate(dimensions); - expect(newDimensions.width).toBe(Math.round(width + 104)); - expect(newDimensions.height).toBe(Math.round(height + 104)); - }); - it_fails_ffox('should select the text with mouse', async({page, server}) => { - await page.goto(server.PREFIX + '/input/textarea.html'); - await page.focus('textarea'); - const text = 'This is the text that we are going to try to select. Let\'s see how it goes.'; - await page.keyboard.type(text); - // Firefox needs an extra frame here after typing or it will fail to set the scrollTop - await page.evaluate(() => new Promise(requestAnimationFrame)); - await page.evaluate(() => document.querySelector('textarea').scrollTop = 0); - const {x, y} = await page.evaluate(dimensions); - await page.mouse.move(x + 2,y + 2); - await page.mouse.down(); - await page.mouse.move(100,100); - await page.mouse.up(); - expect(await page.evaluate(() => { - const textarea = document.querySelector('textarea'); - return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); - })).toBe(text); - }); - it_fails_ffox('should trigger hover state', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.hover('#button-6'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6'); - await page.hover('#button-2'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2'); - await page.hover('#button-91'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91'); }); - it_fails_ffox('should trigger hover state with removed window.Node', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.evaluate(() => delete window.Node); - await page.hover('#button-6'); - expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6'); - }); - it('should set modifier keys on click', async({page, server}) => { - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true)); - const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'}; - // In Firefox, the Meta modifier only exists on Mac - if (FFOX && os.platform() !== 'darwin') - delete modifiers['Meta']; - for (const modifier in modifiers) { - await page.keyboard.down(modifier); - await page.click('#button-3'); - if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) - throw new Error(modifiers[modifier] + ' should be true'); - await page.keyboard.up(modifier); - } + await page.mouse.click(50, 60); + const event = await page.evaluate(() => window.clickPromise); + expect(event.type).toBe('click'); + expect(event.detail).toBe(1); + expect(event.clientX).toBe(50); + expect(event.clientY).toBe(60); + expect(event.isTrusted).toBe(true); + expect(event.button).toBe(0); + }); + itFailsFirefox('should resize the textarea', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + const {x, y, width, height} = await page.evaluate(dimensions); + const mouse = page.mouse; + await mouse.move(x + width - 4, y + height - 4); + await mouse.down(); + await mouse.move(x + width + 100, y + height + 100); + await mouse.up(); + const newDimensions = await page.evaluate(dimensions); + expect(newDimensions.width).toBe(Math.round(width + 104)); + expect(newDimensions.height).toBe(Math.round(height + 104)); + }); + itFailsFirefox('should select the text with mouse', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/textarea.html'); + await page.focus('textarea'); + const text = 'This is the text that we are going to try to select. Let\'s see how it goes.'; + await page.keyboard.type(text); + // Firefox needs an extra frame here after typing or it will fail to set the scrollTop + await page.evaluate(() => new Promise(requestAnimationFrame)); + await page.evaluate(() => document.querySelector('textarea').scrollTop = 0); + const {x, y} = await page.evaluate(dimensions); + await page.mouse.move(x + 2,y + 2); + await page.mouse.down(); + await page.mouse.move(100,100); + await page.mouse.up(); + expect(await page.evaluate(() => { + const textarea = document.querySelector('textarea'); + return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd); + })).toBe(text); + }); + itFailsFirefox('should trigger hover state', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/scrollable.html'); + await page.hover('#button-6'); + expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6'); + await page.hover('#button-2'); + expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2'); + await page.hover('#button-91'); + expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91'); + }); + itFailsFirefox('should trigger hover state with removed window.Node', async() => { + const { page, server } = getTestState(); + + await page.goto(server.PREFIX + '/input/scrollable.html'); + await page.evaluate(() => delete window.Node); + await page.hover('#button-6'); + expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6'); + }); + it('should set modifier keys on click', async() => { + const { page, server, isFirefox } = getTestState(); + + await page.goto(server.PREFIX + '/input/scrollable.html'); + await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true)); + const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'}; + // In Firefox, the Meta modifier only exists on Mac + if (isFirefox && os.platform() !== 'darwin') + delete modifiers['Meta']; + for (const modifier in modifiers) { + await page.keyboard.down(modifier); await page.click('#button-3'); - for (const modifier in modifiers) { - if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) - throw new Error(modifiers[modifier] + ' should be false'); - } - }); - it_fails_ffox('should tween mouse movement', async({page, server}) => { - await page.mouse.move(100, 100); - await page.evaluate(() => { - window.result = []; - document.addEventListener('mousemove', event => { - window.result.push([event.clientX, event.clientY]); - }); + if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) + throw new Error(modifiers[modifier] + ' should be true'); + await page.keyboard.up(modifier); + } + await page.click('#button-3'); + for (const modifier in modifiers) { + if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier]))) + throw new Error(modifiers[modifier] + ' should be false'); + } + }); + itFailsFirefox('should tween mouse movement', async() => { + const { page } = getTestState(); + + await page.mouse.move(100, 100); + await page.evaluate(() => { + window.result = []; + document.addEventListener('mousemove', event => { + window.result.push([event.clientX, event.clientY]); }); - await page.mouse.move(200, 300, {steps: 5}); - expect(await page.evaluate('result')).toEqual([ - [120, 140], - [140, 180], - [160, 220], - [180, 260], - [200, 300] - ]); }); - // @see https://crbug.com/929806 - it_fails_ffox('should work with mobile viewports and cross process navigations', async({page, server}) => { - await page.goto(server.EMPTY_PAGE); - await page.setViewport({width: 360, height: 640, isMobile: true}); - await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html'); - await page.evaluate(() => { - document.addEventListener('click', event => { - window.result = {x: event.clientX, y: event.clientY}; - }); + await page.mouse.move(200, 300, {steps: 5}); + expect(await page.evaluate('result')).toEqual([ + [120, 140], + [140, 180], + [160, 220], + [180, 260], + [200, 300] + ]); + }); + // @see https://crbug.com/929806 + itFailsFirefox('should work with mobile viewports and cross process navigations', async() => { + const { page, server } = getTestState(); + + await page.goto(server.EMPTY_PAGE); + await page.setViewport({width: 360, height: 640, isMobile: true}); + await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html'); + await page.evaluate(() => { + document.addEventListener('click', event => { + window.result = {x: event.clientX, y: event.clientY}; }); + }); - await page.mouse.click(30, 40); + await page.mouse.click(30, 40); - expect(await page.evaluate('result')).toEqual({x: 30, y: 40}); - }); + expect(await page.evaluate('result')).toEqual({x: 30, y: 40}); }); -}; +}); diff --git a/test/navigation.spec.js b/test/navigation.spec.js index 934b397651a69..c00675baf0612 100644 --- a/test/navigation.spec.js +++ b/test/navigation.spec.js @@ -15,18 +15,22 @@ */ const utils = require('./utils'); +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - +describe('navigation', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Page.goto', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE); }); - it_fails_ffox('should work with anchor navigation', async({page, server}) => { + itFailsFirefox('should work with anchor navigation', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE + '#foo'); @@ -34,28 +38,38 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { await page.goto(server.EMPTY_PAGE + '#bar'); expect(page.url()).toBe(server.EMPTY_PAGE + '#bar'); }); - it('should work with redirects', async({page, server}) => { + it('should work with redirects', async() => { + const { page, server } = getTestState(); + server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/empty.html'); await page.goto(server.PREFIX + '/redirect/1.html'); expect(page.url()).toBe(server.EMPTY_PAGE); }); - it('should navigate to about:blank', async({page, server}) => { + it('should navigate to about:blank', async() => { + const { page } = getTestState(); + const response = await page.goto('about:blank'); expect(response).toBe(null); }); - it_fails_ffox('should return response when page changes its URL after load', async({page, server}) => { + itFailsFirefox('should return response when page changes its URL after load', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.PREFIX + '/historyapi.html'); expect(response.status()).toBe(200); }); - it('should work with subframes return 204', async({page, server}) => { + it('should work with subframes return 204', async() => { + const { page, server } = getTestState(); + server.setRoute('/frames/frame.html', (req, res) => { res.statusCode = 204; res.end(); }); await page.goto(server.PREFIX + '/frames/one-frame.html'); }); - it_fails_ffox('should fail when server returns 204', async({page, server}) => { + itFailsFirefox('should fail when server returns 204', async() => { + const { page, server, isChrome } = getTestState(); + server.setRoute('/empty.html', (req, res) => { res.statusCode = 204; res.end(); @@ -63,16 +77,20 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { let error = null; await page.goto(server.EMPTY_PAGE).catch(e => error = e); expect(error).not.toBe(null); - if (CHROME) + if (isChrome) expect(error.message).toContain('net::ERR_ABORTED'); else expect(error.message).toContain('NS_BINDING_ABORTED'); }); - it_fails_ffox('should navigate to empty page with domcontentloaded', async({page, server}) => { + itFailsFirefox('should navigate to empty page with domcontentloaded', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'}); expect(response.status()).toBe(200); }); - it_fails_ffox('should work when page calls history API in beforeunload', async({page, server}) => { + itFailsFirefox('should work when page calls history API in beforeunload', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false); @@ -80,23 +98,31 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { const response = await page.goto(server.PREFIX + '/grid.html'); expect(response.status()).toBe(200); }); - it_fails_ffox('should navigate to empty page with networkidle0', async({page, server}) => { + itFailsFirefox('should navigate to empty page with networkidle0', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle0'}); expect(response.status()).toBe(200); }); - it_fails_ffox('should navigate to empty page with networkidle2', async({page, server}) => { + itFailsFirefox('should navigate to empty page with networkidle2', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle2'}); expect(response.status()).toBe(200); }); - it_fails_ffox('should fail when navigating to bad url', async({page, server}) => { + itFailsFirefox('should fail when navigating to bad url', async() => { + const { page, isChrome } = getTestState(); + let error = null; await page.goto('asdfasdf').catch(e => error = e); - if (CHROME) + if (isChrome) expect(error.message).toContain('Cannot navigate to invalid URL'); else expect(error.message).toContain('Invalid url'); }); - it_fails_ffox('should fail when navigating to bad SSL', async({page, httpsServer}) => { + itFailsFirefox('should fail when navigating to bad SSL', async() => { + const { page, httpsServer, isChrome } = getTestState(); + // Make sure that network events do not emit 'undefined'. // @see https://crbug.com/750469 page.on('request', request => expect(request).toBeTruthy()); @@ -104,35 +130,43 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { page.on('requestfailed', request => expect(request).toBeTruthy()); let error = null; await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); - if (CHROME) + if (isChrome) expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID'); else expect(error.message).toContain('SSL_ERROR_UNKNOWN'); }); - it_fails_ffox('should fail when navigating to bad SSL after redirects', async({page, server, httpsServer}) => { + itFailsFirefox('should fail when navigating to bad SSL after redirects', async() => { + const { page, server, httpsServer, isChrome } = getTestState(); + server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/empty.html'); let error = null; await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e); - if (CHROME) + if (isChrome) expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID'); else expect(error.message).toContain('SSL_ERROR_UNKNOWN'); }); - it('should throw if networkidle is passed as an option', async({page, server}) => { + it('should throw if networkidle is passed as an option', async() => { + const { page, server } = getTestState(); + let error = null; await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle'}).catch(err => error = err); expect(error.message).toContain('"networkidle" option is no longer supported'); }); - it_fails_ffox('should fail when main resources failed to load', async({page, server}) => { + itFailsFirefox('should fail when main resources failed to load', async() => { + const { page, isChrome } = getTestState(); + let error = null; await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e); - if (CHROME) + if (isChrome) expect(error.message).toContain('net::ERR_CONNECTION_REFUSED'); else expect(error.message).toContain('NS_ERROR_CONNECTION_REFUSED'); }); - it('should fail when exceeding maximum navigation timeout', async({page, server}) => { + it('should fail when exceeding maximum navigation timeout', async() => { + const { page, server, puppeteer } = getTestState(); + // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; @@ -140,7 +174,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should fail when exceeding default maximum navigation timeout', async({page, server}) => { + it('should fail when exceeding default maximum navigation timeout', async() => { + const { page, server, puppeteer } = getTestState(); + // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; @@ -149,7 +185,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should fail when exceeding default maximum timeout', async({page, server}) => { + it('should fail when exceeding default maximum timeout', async() => { + const { page, server, puppeteer } = getTestState(); + // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; @@ -158,7 +196,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should prioritize default navigation timeout over default timeout', async({page, server}) => { + it('should prioritize default navigation timeout over default timeout', async() => { + const { page, server, puppeteer } = getTestState(); + // Hang for request to the empty.html server.setRoute('/empty.html', (req, res) => { }); let error = null; @@ -168,7 +208,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(error.message).toContain('Navigation timeout of 1 ms exceeded'); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should disable timeout when its set to 0', async({page, server}) => { + it('should disable timeout when its set to 0', async() => { + const { page, server } = getTestState(); + let error = null; let loaded = false; page.once('load', () => loaded = true); @@ -176,20 +218,28 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(error).toBe(null); expect(loaded).toBe(true); }); - it_fails_ffox('should work when navigating to valid url', async({page, server}) => { + itFailsFirefox('should work when navigating to valid url', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE); expect(response.ok()).toBe(true); }); - it_fails_ffox('should work when navigating to data url', async({page, server}) => { + itFailsFirefox('should work when navigating to data url', async() => { + const { page } = getTestState(); + const response = await page.goto('data:text/html,hello'); expect(response.ok()).toBe(true); }); - it_fails_ffox('should work when navigating to 404', async({page, server}) => { + itFailsFirefox('should work when navigating to 404', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.PREFIX + '/not-found'); expect(response.ok()).toBe(false); expect(response.status()).toBe(404); }); - it_fails_ffox('should return last response in redirect chain', async({page, server}) => { + itFailsFirefox('should return last response in redirect chain', async() => { + const { page, server } = getTestState(); + server.setRedirect('/redirect/1.html', '/redirect/2.html'); server.setRedirect('/redirect/2.html', '/redirect/3.html'); server.setRedirect('/redirect/3.html', server.EMPTY_PAGE); @@ -197,7 +247,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response.ok()).toBe(true); expect(response.url()).toBe(server.EMPTY_PAGE); }); - it_fails_ffox('should wait for network idle to succeed navigation', async({page, server}) => { + itFailsFirefox('should wait for network idle to succeed navigation', async() => { + const { page, server } = getTestState(); + let responses = []; // Hold on to a bunch of requests without answering. server.setRoute('/fetch-request-a.js', (req, res) => responses.push(res)); @@ -254,7 +306,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { // Expect navigation to succeed. expect(response.ok()).toBe(true); }); - it('should not leak listeners during navigation', async({page, server}) => { + it('should not leak listeners during navigation', async() => { + const { page, server } = getTestState(); + let warning = null; const warningHandler = w => warning = w; process.on('warning', warningHandler); @@ -263,7 +317,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { process.removeListener('warning', warningHandler); expect(warning).toBe(null); }); - it_fails_ffox('should not leak listeners during bad navigation', async({page, server}) => { + itFailsFirefox('should not leak listeners during bad navigation', async() => { + const { page } = getTestState(); + let warning = null; const warningHandler = w => warning = w; process.on('warning', warningHandler); @@ -272,7 +328,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { process.removeListener('warning', warningHandler); expect(warning).toBe(null); }); - it('should not leak listeners during navigation of 11 pages', async({page, context, server}) => { + it('should not leak listeners during navigation of 11 pages', async() => { + const { context, server } = getTestState(); + let warning = null; const warningHandler = w => warning = w; process.on('warning', warningHandler); @@ -284,7 +342,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { process.removeListener('warning', warningHandler); expect(warning).toBe(null); }); - it_fails_ffox('should navigate to dataURL and fire dataURL requests', async({page, server}) => { + itFailsFirefox('should navigate to dataURL and fire dataURL requests', async() => { + const { page } = getTestState(); + const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); const dataURL = 'data:text/html,
yo
'; @@ -293,7 +353,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(requests.length).toBe(1); expect(requests[0].url()).toBe(dataURL); }); - it_fails_ffox('should navigate to URL with hash and fire requests without hash', async({page, server}) => { + itFailsFirefox('should navigate to URL with hash and fire requests without hash', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); const response = await page.goto(server.EMPTY_PAGE + '#hash'); @@ -302,12 +364,16 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(requests.length).toBe(1); expect(requests[0].url()).toBe(server.EMPTY_PAGE); }); - it_fails_ffox('should work with self requesting page', async({page, server}) => { + itFailsFirefox('should work with self requesting page', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.PREFIX + '/self-request.html'); expect(response.status()).toBe(200); expect(response.url()).toContain('self-request.html'); }); - it_fails_ffox('should fail when navigating and show the url at the error message', async function({page, server, httpsServer}) { + itFailsFirefox('should fail when navigating and show the url at the error message', async() => { + const { page, httpsServer } = getTestState(); + const url = httpsServer.PREFIX + '/redirect/1.html'; let error = null; try { @@ -317,7 +383,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { } expect(error.message).toContain(url); }); - it_fails_ffox('should send referer', async({page, server}) => { + itFailsFirefox('should send referer', async() => { + const { page, server } = getTestState(); + const [request1, request2] = await Promise.all([ server.waitForRequest('/grid.html'), server.waitForRequest('/digits/1.png'), @@ -332,7 +400,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { }); describe('Page.waitForNavigation', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ page.waitForNavigation(), @@ -341,7 +411,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response.ok()).toBe(true); expect(response.url()).toContain('grid.html'); }); - it('should work with both domcontentloaded and load', async({page, server}) => { + it('should work with both domcontentloaded and load', async() => { + const { page, server } = getTestState(); + let response = null; server.setRoute('/one-style.css', (req, res) => response = res); const navigationPromise = page.goto(server.PREFIX + '/one-style.html'); @@ -361,7 +433,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { await bothFiredPromise; await navigationPromise; }); - it_fails_ffox('should work with clicking on anchor links', async({page, server}) => { + itFailsFirefox('should work with clicking on anchor links', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent(`foobar`); const [response] = await Promise.all([ @@ -371,7 +445,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response).toBe(null); expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar'); }); - it_fails_ffox('should work with history.pushState()', async({page, server}) => { + itFailsFirefox('should work with history.pushState()', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent(` SPA @@ -386,7 +462,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response).toBe(null); expect(page.url()).toBe(server.PREFIX + '/wow.html'); }); - it_fails_ffox('should work with history.replaceState()', async({page, server}) => { + itFailsFirefox('should work with history.replaceState()', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent(` SPA @@ -401,7 +479,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response).toBe(null); expect(page.url()).toBe(server.PREFIX + '/replaced.html'); }); - it_fails_ffox('should work with DOM history.back()/history.forward()', async({page, server}) => { + itFailsFirefox('should work with DOM history.back()/history.forward()', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent(` back @@ -427,7 +507,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(forwardResponse).toBe(null); expect(page.url()).toBe(server.PREFIX + '/second.html'); }); - it_fails_ffox('should work when subframe issues window.stop()', async({page, server}) => { + itFailsFirefox('should work when subframe issues window.stop()', async() => { + const { page, server } = getTestState(); + server.setRoute('/frames/style.css', (req, res) => {}); const navigationPromise = page.goto(server.PREFIX + '/frames/one-frame.html'); const frame = await utils.waitEvent(page, 'frameattached'); @@ -444,8 +526,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { }); }); - describe_fails_ffox('Page.goBack', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.goBack', function() { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.goto(server.PREFIX + '/grid.html'); @@ -460,7 +544,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { response = await page.goForward(); expect(response).toBe(null); }); - it('should work with HistoryAPI', async({page, server}) => { + it('should work with HistoryAPI', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { history.pushState({}, '', '/first.html'); @@ -477,8 +563,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { }); }); - describe_fails_ffox('Frame.goto', function() { - it('should navigate subframes', async({page, server}) => { + describeFailsFirefox('Frame.goto', function() { + it('should navigate subframes', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames()[0].url()).toContain('/frames/one-frame.html'); expect(page.frames()[1].url()).toContain('/frames/frame.html'); @@ -487,7 +575,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response.ok()).toBe(true); expect(response.frame()).toBe(page.frames()[1]); }); - it('should reject when frame detaches', async({page, server}) => { + it('should reject when frame detaches', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/one-frame.html'); server.setRoute('/empty.html', () => {}); @@ -498,7 +588,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { const error = await navigationPromise; expect(error.message).toBe('Navigating frame was detached'); }); - it('should return matching responses', async({page, server}) => { + it('should return matching responses', async() => { + const { page, server } = getTestState(); + // Disable cache: otherwise, chromium will cache similar requests. await page.setCacheEnabled(false); await page.goto(server.EMPTY_PAGE); @@ -527,8 +619,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { }); }); - describe_fails_ffox('Frame.waitForNavigation', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Frame.waitForNavigation', function() { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/one-frame.html'); const frame = page.frames()[1]; const [response] = await Promise.all([ @@ -540,7 +634,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { expect(response.frame()).toBe(frame); expect(page.url()).toContain('/frames/one-frame.html'); }); - it('should fail when frame detaches', async({page, server}) => { + it('should fail when frame detaches', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/one-frame.html'); const frame = page.frames()[1]; @@ -558,11 +654,13 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { }); describe('Page.reload', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => window._foo = 10); await page.reload(); expect(await page.evaluate(() => window._foo)).toBe(undefined); }); }); -}; +}); diff --git a/test/network.spec.js b/test/network.spec.js index bb19f0299e44d..053a82e08891a 100644 --- a/test/network.spec.js +++ b/test/network.spec.js @@ -17,27 +17,34 @@ const fs = require('fs'); const path = require('path'); const utils = require('./utils'); +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect, CHROME}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; +describe('network', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Page.Events.Request', function() { - it('should fire for navigation requests', async({page, server}) => { + it('should fire for navigation requests', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); await page.goto(server.EMPTY_PAGE); expect(requests.length).toBe(1); }); - it_fails_ffox('should fire for iframes', async({page, server}) => { + itFailsFirefox('should fire for iframes', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); expect(requests.length).toBe(2); }); - it('should fire for fetches', async({page, server}) => { + it('should fire for fetches', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); await page.goto(server.EMPTY_PAGE); @@ -47,14 +54,18 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('Request.frame', function() { - it('should work for main frame navigation request', async({page, server}) => { + it('should work for main frame navigation request', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); await page.goto(server.EMPTY_PAGE); expect(requests.length).toBe(1); expect(requests[0].frame()).toBe(page.mainFrame()); }); - it_fails_ffox('should work for subframe navigation request', async({page, server}) => { + itFailsFirefox('should work for subframe navigation request', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); @@ -62,7 +73,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(requests.length).toBe(1); expect(requests[0].frame()).toBe(page.frames()[1]); }); - it('should work for fetch requests', async({page, server}) => { + it('should work for fetch requests', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); let requests = []; page.on('request', request => !utils.isFavicon(request) && requests.push(request)); @@ -73,18 +86,22 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Request.headers', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Request.headers', function() { + it('should work', async() => { + const { page, server, isChrome } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE); - if (CHROME) + if (isChrome) expect(response.request().headers()['user-agent']).toContain('Chrome'); else expect(response.request().headers()['user-agent']).toContain('Firefox'); }); }); - describe_fails_ffox('Response.headers', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Response.headers', function() { + it('should work', async() => { + const { page, server } = getTestState(); + server.setRoute('/empty.html', (req, res) => { res.setHeader('foo', 'bar'); res.end(); @@ -94,13 +111,17 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Response.fromCache', function() { - it('should return |false| for non-cached content', async({page, server}) => { + describeFailsFirefox('Response.fromCache', function() { + it('should return |false| for non-cached content', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE); expect(response.fromCache()).toBe(false); }); - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + const responses = new Map(); page.on('response', r => !utils.isFavicon(r.request()) && responses.set(r.url().split('/').pop(), r)); @@ -116,13 +137,17 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Response.fromServiceWorker', function() { - it('should return |false| for non-service-worker content', async({page, server}) => { + describeFailsFirefox('Response.fromServiceWorker', function() { + it('should return |false| for non-service-worker content', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE); expect(response.fromServiceWorker()).toBe(false); }); - it('Response.fromServiceWorker', async({page, server}) => { + it('Response.fromServiceWorker', async() => { + const { page, server } = getTestState(); + const responses = new Map(); page.on('response', r => responses.set(r.url().split('/').pop(), r)); @@ -139,8 +164,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Request.postData', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Request.postData', function() { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); server.setRoute('/post', (req, res) => res.end()); let request = null; @@ -149,24 +176,32 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(request).toBeTruthy(); expect(request.postData()).toBe('{"foo":"bar"}'); }); - it('should be |undefined| when there is no post data', async({page, server}) => { + it('should be |undefined| when there is no post data', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.EMPTY_PAGE); expect(response.request().postData()).toBe(undefined); }); }); - describe_fails_ffox('Response.text', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Response.text', function() { + it('should work', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.PREFIX + '/simple.json'); expect(await response.text()).toBe('{"foo": "bar"}\n'); }); - it('should return uncompressed text', async({page, server}) => { + it('should return uncompressed text', async() => { + const { page, server } = getTestState(); + server.enableGzip('/simple.json'); const response = await page.goto(server.PREFIX + '/simple.json'); expect(response.headers()['content-encoding']).toBe('gzip'); expect(await response.text()).toBe('{"foo": "bar"}\n'); }); - it('should throw when requesting body of redirected response', async({page, server}) => { + it('should throw when requesting body of redirected response', async() => { + const { page, server } = getTestState(); + server.setRedirect('/foo.html', '/empty.html'); const response = await page.goto(server.PREFIX + '/foo.html'); const redirectChain = response.request().redirectChain(); @@ -177,7 +212,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { await redirected.text().catch(e => error = e); expect(error.message).toContain('Response body is unavailable for redirect responses'); }); - it('should wait until response completes', async({page, server}) => { + it('should wait until response completes', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); // Setup server to trap request. let serverResponse = null; @@ -212,21 +249,27 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Response.json', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Response.json', function() { + it('should work', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.PREFIX + '/simple.json'); expect(await response.json()).toEqual({foo: 'bar'}); }); }); - describe_fails_ffox('Response.buffer', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Response.buffer', function() { + it('should work', async() => { + const { page, server } = getTestState(); + const response = await page.goto(server.PREFIX + '/pptr.png'); const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); const responseBuffer = await response.buffer(); expect(responseBuffer.equals(imageBuffer)).toBe(true); }); - it('should work with compression', async({page, server}) => { + it('should work with compression', async() => { + const { page, server } = getTestState(); + server.enableGzip('/pptr.png'); const response = await page.goto(server.PREFIX + '/pptr.png'); const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); @@ -235,8 +278,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Response.statusText', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Response.statusText', function() { + it('should work', async() => { + const { page, server } = getTestState(); + server.setRoute('/cool', (req, res) => { res.writeHead(200, 'cool!'); res.end(); @@ -246,8 +291,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Network Events', function() { - it('Page.Events.Request', async({page, server}) => { + describeFailsFirefox('Network Events', function() { + it('Page.Events.Request', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => requests.push(request)); await page.goto(server.EMPTY_PAGE); @@ -259,7 +306,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(requests[0].frame() === page.mainFrame()).toBe(true); expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); }); - it('Page.Events.Response', async({page, server}) => { + it('Page.Events.Response', async() => { + const { page, server } = getTestState(); + const responses = []; page.on('response', response => responses.push(response)); await page.goto(server.EMPTY_PAGE); @@ -274,7 +323,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(remoteAddress.port).toBe(server.PORT); }); - it('Page.Events.RequestFailed', async({page, server}) => { + it('Page.Events.RequestFailed', async() => { + const { page, server, isChrome } = getTestState(); + await page.setRequestInterception(true); page.on('request', request => { if (request.url().endsWith('css')) @@ -289,13 +340,15 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(failedRequests[0].url()).toContain('one-style.css'); expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].resourceType()).toBe('stylesheet'); - if (CHROME) + if (isChrome) expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); else expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].frame()).toBeTruthy(); }); - it('Page.Events.RequestFinished', async({page, server}) => { + it('Page.Events.RequestFinished', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('requestfinished', request => requests.push(request)); await page.goto(server.EMPTY_PAGE); @@ -305,7 +358,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(requests[0].frame() === page.mainFrame()).toBe(true); expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); }); - it('should fire events in proper order', async({page, server}) => { + it('should fire events in proper order', async() => { + const { page, server } = getTestState(); + const events = []; page.on('request', request => events.push('request')); page.on('response', response => events.push('response')); @@ -313,7 +368,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { await page.goto(server.EMPTY_PAGE); expect(events).toEqual(['request', 'response', 'requestfinished']); }); - it('should support redirects', async({page, server}) => { + it('should support redirects', async() => { + const { page, server } = getTestState(); + const events = []; page.on('request', request => events.push(`${request.method()} ${request.url()}`)); page.on('response', response => events.push(`${response.status()} ${response.url()}`)); @@ -340,7 +397,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); describe('Request.isNavigationRequest', () => { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + const requests = new Map(); page.on('request', request => requests.set(request.url().split('/').pop(), request)); server.setRedirect('/rrredirect', '/frames/one-frame.html'); @@ -351,7 +410,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(requests.get('script.js').isNavigationRequest()).toBe(false); expect(requests.get('style.css').isNavigationRequest()).toBe(false); }); - it_fails_ffox('should work with request interception', async({page, server}) => { + itFailsFirefox('should work with request interception', async() => { + const { page, server } = getTestState(); + const requests = new Map(); page.on('request', request => { requests.set(request.url().split('/').pop(), request); @@ -366,7 +427,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(requests.get('script.js').isNavigationRequest()).toBe(false); expect(requests.get('style.css').isNavigationRequest()).toBe(false); }); - it('should work when navigating to image', async({page, server}) => { + it('should work when navigating to image', async() => { + const { page, server } = getTestState(); + const requests = []; page.on('request', request => requests.push(request)); await page.goto(server.PREFIX + '/pptr.png'); @@ -374,8 +437,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Page.setExtraHTTPHeaders', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.setExtraHTTPHeaders', function() { + it('should work', async() => { + const { page, server } = getTestState(); + await page.setExtraHTTPHeaders({ foo: 'bar' }); @@ -385,7 +450,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { ]); expect(request.headers['foo']).toBe('bar'); }); - it('should throw for non-string header values', async({page, server}) => { + it('should throw for non-string header values', async() => { + const { page } = getTestState(); + let error = null; try { await page.setExtraHTTPHeaders({ 'foo': 1 }); @@ -396,8 +463,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { }); }); - describe_fails_ffox('Page.authenticate', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.authenticate', function() { + it('should work', async() => { + const { page, server } = getTestState(); + server.setAuth('/empty.html', 'user', 'pass'); let response = await page.goto(server.EMPTY_PAGE); expect(response.status()).toBe(401); @@ -408,7 +477,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { response = await page.reload(); expect(response.status()).toBe(200); }); - it('should fail if wrong credentials', async({page, server}) => { + it('should fail if wrong credentials', async() => { + const { page, server } = getTestState(); + // Use unique user/password since Chrome caches credentials per origin. server.setAuth('/empty.html', 'user2', 'pass2'); await page.authenticate({ @@ -418,7 +489,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { const response = await page.goto(server.EMPTY_PAGE); expect(response.status()).toBe(401); }); - it('should allow disable authentication', async({page, server}) => { + it('should allow disable authentication', async() => { + const { page, server } = getTestState(); + // Use unique user/password since Chrome caches credentials per origin. server.setAuth('/empty.html', 'user3', 'pass3'); await page.authenticate({ @@ -433,5 +506,5 @@ module.exports.addTests = function({testRunner, expect, CHROME}) { expect(response.status()).toBe(401); }); }); -}; +}); diff --git a/test/oopif.spec.js b/test/oopif.spec.js index f3a4c3391c01a..5f7f940d37082 100644 --- a/test/oopif.spec.js +++ b/test/oopif.spec.js @@ -14,43 +14,53 @@ * limitations under the License. */ -module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - - describe('OOPIF', function() { - beforeAll(async function(state) { - state.browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { - args: (defaultBrowserOptions.args || []).concat(['--site-per-process']), - })); - }); - beforeEach(async function(state) { - state.context = await state.browser.createIncognitoBrowserContext(); - state.page = await state.context.newPage(); - }); - afterEach(async function(state) { - await state.context.close(); - state.page = null; - state.context = null; - }); - afterAll(async function(state) { - await state.browser.close(); - state.browser = null; - }); - xit('should report oopif frames', async function({page, server, context}) { - await page.goto(server.PREFIX + '/dynamic-oopif.html'); - expect(oopifs(context).length).toBe(1); - expect(page.frames().length).toBe(2); - }); - it('should load oopif iframes with subresources and request interception', async function({page, server, context}) { - await page.setRequestInterception(true); - page.on('request', request => request.continue()); - await page.goto(server.PREFIX + '/dynamic-oopif.html'); - expect(oopifs(context).length).toBe(1); - }); +const expect = require('expect'); +const {getTestState} = require('./mocha-utils'); + +describeChromeOnly('OOPIF', function() { + /* We use a special browser for this test as we need the --site-per-process flag */ + let browser; + let context; + let page; + + before(async() => { + const {puppeteer, defaultBrowserOptions} = getTestState(); + browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { + args: (defaultBrowserOptions.args || []).concat(['--site-per-process']), + })); + }); + + beforeEach(async() => { + context = await browser.createIncognitoBrowserContext(); + page = await context.newPage(); + }); + + afterEach(async() => { + await context.close(); + page = null; + context = null; + }); + + after(async() => { + await browser.close(); + browser = null; + }); + xit('should report oopif frames', async() => { + const { server } = getTestState(); + + await page.goto(server.PREFIX + '/dynamic-oopif.html'); + expect(oopifs(context).length).toBe(1); + expect(page.frames().length).toBe(2); + }); + it('should load oopif iframes with subresources and request interception', async() => { + const { server } = getTestState(); + + await page.setRequestInterception(true); + page.on('request', request => request.continue()); + await page.goto(server.PREFIX + '/dynamic-oopif.html'); + expect(oopifs(context).length).toBe(1); }); -}; +}); /** diff --git a/test/page.spec.js b/test/page.spec.js index 908799f45a1e2..bfcb702d96ade 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -17,14 +17,16 @@ const fs = require('fs'); const path = require('path'); const utils = require('./utils'); const {waitEvent} = utils; +const expect = require('expect'); +const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils'); -module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHROME}) { - const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; - const {it, fit, xit, it_fails_ffox} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; - +describe('Page', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); describe('Page.close', function() { - it('should reject all promises when page is closed', async({context}) => { + it('should reject all promises when page is closed', async() => { + const { context } = getTestState(); + const newPage = await context.newPage(); let error = null; await Promise.all([ @@ -33,13 +35,17 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(error.message).toContain('Protocol error'); }); - it('should not be visible in browser.pages', async({browser}) => { + it('should not be visible in browser.pages', async() => { + const { browser } = getTestState(); + const newPage = await browser.newPage(); expect(await browser.pages()).toContain(newPage); await newPage.close(); expect(await browser.pages()).not.toContain(newPage); }); - it_fails_ffox('should run beforeunload if asked for', async({context, server}) => { + itFailsFirefox('should run beforeunload if asked for', async() => { + const { context, server, isChrome } = getTestState(); + const newPage = await context.newPage(); await newPage.goto(server.PREFIX + '/beforeunload.html'); // We have to interact with a page so that 'beforeunload' handlers @@ -49,14 +55,16 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR const dialog = await waitEvent(newPage, 'dialog'); expect(dialog.type()).toBe('beforeunload'); expect(dialog.defaultValue()).toBe(''); - if (CHROME) + if (isChrome) expect(dialog.message()).toBe(''); else expect(dialog.message()).toBe('This page is asking you to confirm that you want to leave - data you have entered may not be saved.'); await dialog.accept(); await pageClosingPromise; }); - it_fails_ffox('should *not* run beforeunload by default', async({context, server}) => { + itFailsFirefox('should *not* run beforeunload by default', async() => { + const { context, server } = getTestState(); + const newPage = await context.newPage(); await newPage.goto(server.PREFIX + '/beforeunload.html'); // We have to interact with a page so that 'beforeunload' handlers @@ -64,13 +72,17 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR await newPage.click('body'); await newPage.close(); }); - it('should set the page close state', async({context}) => { + it('should set the page close state', async() => { + const { context } = getTestState(); + const newPage = await context.newPage(); expect(newPage.isClosed()).toBe(false); await newPage.close(); expect(newPage.isClosed()).toBe(true); }); - it_fails_ffox('should terminate network waiters', async({context, server}) => { + itFailsFirefox('should terminate network waiters', async() => { + const { context, server } = getTestState(); + const newPage = await context.newPage(); const results = await Promise.all([ newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e), @@ -86,7 +98,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.Events.Load', function() { - it('should fire when expected', async({page, server}) => { + it('should fire when expected', async() => { + const { page } = getTestState(); + await Promise.all([ page.goto('about:blank'), utils.waitEvent(page, 'load'), @@ -94,8 +108,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Async stacks', () => { - it('should work', async({page, server}) => { + describeFailsFirefox('Async stacks', () => { + it('should work', async() => { + const { page, server } = getTestState(); + server.setRoute('/empty.html', (req, res) => { res.statusCode = 204; res.end(); @@ -107,8 +123,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.Events.error', function() { - it('should throw when page crashes', async({page}) => { + describeFailsFirefox('Page.Events.error', function() { + it('should throw when page crashes', async() => { + const { page } = getTestState(); + let error = null; page.on('error', err => error = err); page.goto('chrome://crash').catch(e => {}); @@ -117,8 +135,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.Events.Popup', function() { - it('should work', async({page}) => { + describeFailsFirefox('Page.Events.Popup', function() { + it('should work', async() => { + const { page } = getTestState(); + const [popup] = await Promise.all([ new Promise(x => page.once('popup', x)), page.evaluate(() => window.open('about:blank')), @@ -126,7 +146,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await page.evaluate(() => !!window.opener)).toBe(false); expect(await popup.evaluate(() => !!window.opener)).toBe(true); }); - it('should work with noopener', async({page}) => { + it('should work with noopener', async() => { + const { page } = getTestState(); + const [popup] = await Promise.all([ new Promise(x => page.once('popup', x)), page.evaluate(() => window.open('about:blank', null, 'noopener')), @@ -134,7 +156,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await page.evaluate(() => !!window.opener)).toBe(false); expect(await popup.evaluate(() => !!window.opener)).toBe(false); }); - it('should work with clicking target=_blank', async({page, server}) => { + it('should work with clicking target=_blank', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent('yo'); const [popup] = await Promise.all([ @@ -144,7 +168,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await page.evaluate(() => !!window.opener)).toBe(false); expect(await popup.evaluate(() => !!window.opener)).toBe(true); }); - it('should work with fake-clicking target=_blank and rel=noopener', async({page, server}) => { + it('should work with fake-clicking target=_blank and rel=noopener', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent('yo'); const [popup] = await Promise.all([ @@ -154,7 +180,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await page.evaluate(() => !!window.opener)).toBe(false); expect(await popup.evaluate(() => !!window.opener)).toBe(false); }); - it('should work with clicking target=_blank and rel=noopener', async({page, server}) => { + it('should work with clicking target=_blank and rel=noopener', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.setContent('yo'); const [popup] = await Promise.all([ @@ -171,34 +199,46 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR return page.evaluate(name => navigator.permissions.query({name}).then(result => result.state), name); } - it('should be prompt by default', async({page, server, context}) => { + it('should be prompt by default', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); expect(await getPermission(page, 'geolocation')).toBe('prompt'); }); - it_fails_ffox('should deny permission when not listed', async({page, server, context}) => { + itFailsFirefox('should deny permission when not listed', async() => { + const { page, server, context } = getTestState(); + await page.goto(server.EMPTY_PAGE); await context.overridePermissions(server.EMPTY_PAGE, []); expect(await getPermission(page, 'geolocation')).toBe('denied'); }); - it('should fail when bad permission is given', async({page, server, context}) => { + it('should fail when bad permission is given', async() => { + const { page, server, context } = getTestState(); + await page.goto(server.EMPTY_PAGE); let error = null; await context.overridePermissions(server.EMPTY_PAGE, ['foo']).catch(e => error = e); expect(error.message).toBe('Unknown permission: foo'); }); - it_fails_ffox('should grant permission when listed', async({page, server, context}) => { + itFailsFirefox('should grant permission when listed', async() => { + const { page, server, context } = getTestState(); + await page.goto(server.EMPTY_PAGE); await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']); expect(await getPermission(page, 'geolocation')).toBe('granted'); }); - it_fails_ffox('should reset permissions', async({page, server, context}) => { + itFailsFirefox('should reset permissions', async() => { + const { page, server, context } = getTestState(); + await page.goto(server.EMPTY_PAGE); await context.overridePermissions(server.EMPTY_PAGE, ['geolocation']); expect(await getPermission(page, 'geolocation')).toBe('granted'); await context.clearPermissionOverrides(); expect(await getPermission(page, 'geolocation')).toBe('prompt'); }); - it_fails_ffox('should trigger permission onchange', async({page, server, context}) => { + itFailsFirefox('should trigger permission onchange', async() => { + const { page, server, context } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { window.events = []; @@ -217,7 +257,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR await context.clearPermissionOverrides(); expect(await page.evaluate(() => window.events)).toEqual(['prompt', 'denied', 'granted', 'prompt']); }); - it_fails_ffox('should isolate permissions between browser contexs', async({page, server, context, browser}) => { + itFailsFirefox('should isolate permissions between browser contexs', async() => { + const { page, server, context, browser } = getTestState(); + await page.goto(server.EMPTY_PAGE); const otherContext = await browser.createIncognitoBrowserContext(); const otherPage = await otherContext.newPage(); @@ -239,7 +281,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.setGeolocation', function() { - it_fails_ffox('should work', async({page, server, context}) => { + itFailsFirefox('should work', async() => { + const { page, server, context } = getTestState(); + await context.overridePermissions(server.PREFIX, ['geolocation']); await page.goto(server.EMPTY_PAGE); await page.setGeolocation({longitude: 10, latitude: 10}); @@ -251,7 +295,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR longitude: 10 }); }); - it('should throw when invalid longitude', async({page, server, context}) => { + it('should throw when invalid longitude', async() => { + const { page } = getTestState(); + let error = null; try { await page.setGeolocation({longitude: 200, latitude: 10}); @@ -262,8 +308,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.setOfflineMode', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.setOfflineMode', function() { + it('should work', async() => { + const { page, server } = getTestState(); + await page.setOfflineMode(true); let error = null; await page.goto(server.EMPTY_PAGE).catch(e => error = e); @@ -272,7 +320,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR const response = await page.reload(); expect(response.status()).toBe(200); }); - it('should emulate navigator.onLine', async({page, server}) => { + it('should emulate navigator.onLine', async() => { + const { page } = getTestState(); + expect(await page.evaluate(() => window.navigator.onLine)).toBe(true); await page.setOfflineMode(true); expect(await page.evaluate(() => window.navigator.onLine)).toBe(false); @@ -282,7 +332,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('ExecutionContext.queryObjects', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page } = getTestState(); + // Instantiate an object await page.evaluate(() => window.set = new Set(['hello', 'world'])); const prototypeHandle = await page.evaluateHandle(() => Set.prototype); @@ -292,7 +344,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR const values = await page.evaluate(objects => Array.from(objects[0].values()), objectsHandle); expect(values).toEqual(['hello', 'world']); }); - it_fails_ffox('should work for non-blank page', async({page, server}) => { + itFailsFirefox('should work for non-blank page', async() => { + const { page, server } = getTestState(); + // Instantiate an object await page.goto(server.EMPTY_PAGE); await page.evaluate(() => window.set = new Set(['hello', 'world'])); @@ -301,14 +355,18 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR const count = await page.evaluate(objects => objects.length, objectsHandle); expect(count).toBe(1); }); - it('should fail for disposed handles', async({page, server}) => { + it('should fail for disposed handles', async() => { + const { page } = getTestState(); + const prototypeHandle = await page.evaluateHandle(() => HTMLBodyElement.prototype); await prototypeHandle.dispose(); let error = null; await page.queryObjects(prototypeHandle).catch(e => error = e); expect(error.message).toBe('Prototype JSHandle is disposed!'); }); - it('should fail primitive values as prototypes', async({page, server}) => { + it('should fail primitive values as prototypes', async() => { + const { page } = getTestState(); + const prototypeHandle = await page.evaluateHandle(() => 42); let error = null; await page.queryObjects(prototypeHandle).catch(e => error = e); @@ -316,8 +374,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.Events.Console', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.Events.Console', function() { + it('should work', async() => { + const { page } = getTestState(); + let message = null; page.once('console', m => message = m); await Promise.all([ @@ -330,7 +390,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await message.args()[1].jsonValue()).toEqual(5); expect(await message.args()[2].jsonValue()).toEqual({foo: 'bar'}); }); - it('should work for different console API calls', async({page, server}) => { + it('should work for different console API calls', async() => { + const { page } = getTestState(); + const messages = []; page.on('console', msg => messages.push(msg)); // All console events will be reported before `page.evaluate` is finished. @@ -356,7 +418,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR 'JSHandle@promise', ]); }); - it('should not fail for window object', async({page, server}) => { + it('should not fail for window object', async() => { + const { page } = getTestState(); + let message = null; page.once('console', msg => message = msg); await Promise.all([ @@ -365,19 +429,23 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(message.text()).toBe('JSHandle@object'); }); - it('should trigger correct Log', async({page, server}) => { + it('should trigger correct Log', async() => { + const { page, server, isChrome } = getTestState(); + await page.goto('about:blank'); const [message] = await Promise.all([ waitEvent(page, 'console'), page.evaluate(async url => fetch(url).catch(e => {}), server.EMPTY_PAGE) ]); expect(message.text()).toContain('Access-Control-Allow-Origin'); - if (CHROME) + if (isChrome) expect(message.type()).toEqual('error'); else expect(message.type()).toEqual('warn'); }); - it('should have location when fetch fails', async({page, server}) => { + it('should have location when fetch fails', async() => { + const { page, server } = getTestState(); + // The point of this test is to make sure that we report console messages from // Log domain: https://vanilla.aslushnikov.com/?Log.entryAdded await page.goto(server.EMPTY_PAGE); @@ -392,7 +460,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR lineNumber: undefined }); }); - it('should have location for console API calls', async({page, server}) => { + it('should have location for console API calls', async() => { + const { page, server, isChrome} = getTestState(); + await page.goto(server.EMPTY_PAGE); const [message] = await Promise.all([ waitEvent(page, 'console'), @@ -403,11 +473,13 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(message.location()).toEqual({ url: server.PREFIX + '/consolelog.html', lineNumber: 7, - columnNumber: CHROME ? 14 : 6, // console.|log vs |console.log + columnNumber: isChrome ? 14 : 6, // console.|log vs |console.log }); }); // @see https://github.com/puppeteer/puppeteer/issues/3865 - it('should not throw when there are console messages in detached iframes', async({browser, page, server}) => { + it('should not throw when there are console messages in detached iframes', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.evaluate(async() => { // 1. Create a popup that Puppeteer is not connected to. @@ -427,19 +499,25 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.Events.DOMContentLoaded', function() { - it('should fire when expected', async({page, server}) => { + it('should fire when expected', async() => { + const { page } = getTestState(); + page.goto('about:blank'); await waitEvent(page, 'domcontentloaded'); }); }); - describe_fails_ffox('Page.metrics', function() { - it('should get metrics from a page', async({page, server}) => { + describeFailsFirefox('Page.metrics', function() { + it('should get metrics from a page', async() => { + const { page } = getTestState(); + await page.goto('about:blank'); const metrics = await page.metrics(); checkMetrics(metrics); }); - it('metrics event fired on console.timeStamp', async({page, server}) => { + it('metrics event fired on console.timeStamp', async() => { + const { page } = getTestState(); + const metricsPromise = new Promise(fulfill => page.once('metrics', fulfill)); await page.evaluate(() => console.timeStamp('test42')); const metrics = await metricsPromise; @@ -472,7 +550,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.waitForRequest', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ page.waitForRequest(server.PREFIX + '/digits/2.png'), @@ -484,7 +564,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(request.url()).toBe(server.PREFIX + '/digits/2.png'); }); - it('should work with predicate', async({page, server}) => { + it('should work with predicate', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ page.waitForRequest(request => request.url() === server.PREFIX + '/digits/2.png'), @@ -496,18 +578,24 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(request.url()).toBe(server.PREFIX + '/digits/2.png'); }); - it('should respect timeout', async({page, server}) => { + it('should respect timeout', async() => { + const { page, puppeteer } = getTestState(); + let error = null; await page.waitForRequest(() => false, {timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should respect default timeout', async({page, server}) => { + it('should respect default timeout', async() => { + const { page, puppeteer } = getTestState(); + let error = null; page.setDefaultTimeout(1); await page.waitForRequest(() => false).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should work with no timeout', async({page, server}) => { + it('should work with no timeout', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [request] = await Promise.all([ page.waitForRequest(server.PREFIX + '/digits/2.png', {timeout: 0}), @@ -522,7 +610,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.waitForResponse', function() { - it_fails_ffox('should work', async({page, server}) => { + itFailsFirefox('should work', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ page.waitForResponse(server.PREFIX + '/digits/2.png'), @@ -534,18 +624,24 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(response.url()).toBe(server.PREFIX + '/digits/2.png'); }); - it('should respect timeout', async({page, server}) => { + it('should respect timeout', async() => { + const { page, puppeteer } = getTestState(); + let error = null; await page.waitForResponse(() => false, {timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should respect default timeout', async({page, server}) => { + it('should respect default timeout', async() => { + const { page, puppeteer } = getTestState(); + let error = null; page.setDefaultTimeout(1); await page.waitForResponse(() => false).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it_fails_ffox('should work with predicate', async({page, server}) => { + itFailsFirefox('should work with predicate', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ page.waitForResponse(response => response.url() === server.PREFIX + '/digits/2.png'), @@ -557,7 +653,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(response.url()).toBe(server.PREFIX + '/digits/2.png'); }); - it_fails_ffox('should work with no timeout', async({page, server}) => { + itFailsFirefox('should work with no timeout', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const [response] = await Promise.all([ page.waitForResponse(server.PREFIX + '/digits/2.png', {timeout: 0}), @@ -571,8 +669,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.exposeFunction', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.exposeFunction', function() { + it('should work', async() => { + const { page } = getTestState(); + await page.exposeFunction('compute', function(a, b) { return a * b; }); @@ -581,7 +681,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); expect(result).toBe(36); }); - it('should throw exception in page context', async({page, server}) => { + it('should throw exception in page context', async() => { + const { page } = getTestState(); + await page.exposeFunction('woof', function() { throw new Error('WOOF WOOF'); }); @@ -595,7 +697,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(message).toBe('WOOF WOOF'); expect(stack).toContain(__filename); }); - it('should support throwing "null"', async({page, server}) => { + it('should support throwing "null"', async() => { + const { page } = getTestState(); + await page.exposeFunction('woof', function() { throw null; }); @@ -608,7 +712,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); expect(thrown).toBe(null); }); - it('should be callable from-inside evaluateOnNewDocument', async({page, server}) => { + it('should be callable from-inside evaluateOnNewDocument', async() => { + const { page } = getTestState(); + let called = false; await page.exposeFunction('woof', function() { called = true; @@ -617,7 +723,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR await page.reload(); expect(called).toBe(true); }); - it('should survive navigation', async({page, server}) => { + it('should survive navigation', async() => { + const { page, server } = getTestState(); + await page.exposeFunction('compute', function(a, b) { return a * b; }); @@ -628,7 +736,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); expect(result).toBe(36); }); - it('should await returned promise', async({page, server}) => { + it('should await returned promise', async() => { + const { page } = getTestState(); + await page.exposeFunction('compute', function(a, b) { return Promise.resolve(a * b); }); @@ -638,7 +748,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); expect(result).toBe(15); }); - it('should work on frames', async({page, server}) => { + it('should work on frames', async() => { + const { page, server } = getTestState(); + await page.exposeFunction('compute', function(a, b) { return Promise.resolve(a * b); }); @@ -650,7 +762,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); expect(result).toBe(15); }); - it('should work on frames before navigation', async({page, server}) => { + it('should work on frames before navigation', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.exposeFunction('compute', function(a, b) { return Promise.resolve(a * b); @@ -662,7 +776,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); expect(result).toBe(15); }); - it('should work with complex objects', async({page, server}) => { + it('should work with complex objects', async() => { + const { page } = getTestState(); + await page.exposeFunction('complexObject', function(a, b) { return {x: a.x + b.x}; }); @@ -671,8 +787,10 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.Events.PageError', function() { - it('should fire', async({page, server}) => { + describeFailsFirefox('Page.Events.PageError', function() { + it('should fire', async() => { + const { page, server } = getTestState(); + let error = null; page.once('pageerror', e => error = e); await Promise.all([ @@ -684,7 +802,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.setUserAgent', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla'); await page.setUserAgent('foobar'); const [request] = await Promise.all([ @@ -693,7 +813,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(request.headers['user-agent']).toBe('foobar'); }); - it_fails_ffox('should work for subframes', async({page, server}) => { + itFailsFirefox('should work for subframes', async() => { + const { page, server } = getTestState(); + expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla'); await page.setUserAgent('foobar'); const [request] = await Promise.all([ @@ -702,7 +824,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(request.headers['user-agent']).toBe('foobar'); }); - it('should emulate device user-agent', async({page, server}) => { + it('should emulate device user-agent', async() => { + const { page, server, puppeteer } = getTestState(); + await page.goto(server.PREFIX + '/mobile.html'); expect(await page.evaluate(() => navigator.userAgent)).not.toContain('iPhone'); await page.setUserAgent(puppeteer.devices['iPhone 6'].userAgent); @@ -710,27 +834,35 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - describe_fails_ffox('Page.setContent', function() { + describeFailsFirefox('Page.setContent', function() { const expectedOutput = '
hello
'; - it('should work', async({page, server}) => { + it('should work', async() => { + const { page } = getTestState(); + await page.setContent('
hello
'); const result = await page.content(); expect(result).toBe(expectedOutput); }); - it('should work with doctype', async({page, server}) => { + it('should work with doctype', async() => { + const { page } = getTestState(); + const doctype = ''; await page.setContent(`${doctype}
hello
`); const result = await page.content(); expect(result).toBe(`${doctype}${expectedOutput}`); }); - it('should work with HTML 4 doctype', async({page, server}) => { + it('should work with HTML 4 doctype', async() => { + const { page } = getTestState(); + const doctype = ''; await page.setContent(`${doctype}
hello
`); const result = await page.content(); expect(result).toBe(`${doctype}${expectedOutput}`); }); - it('should respect timeout', async({page, server}) => { + it('should respect timeout', async() => { + const { page, server, puppeteer } = getTestState(); + const imgPath = '/img.png'; // stall for image server.setRoute(imgPath, (req, res) => {}); @@ -738,7 +870,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR await page.setContent(``, {timeout: 1}).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should respect default navigation timeout', async({page, server}) => { + it('should respect default navigation timeout', async() => { + const { page, server, puppeteer } = getTestState(); + page.setDefaultNavigationTimeout(1); const imgPath = '/img.png'; // stall for image @@ -747,7 +881,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR await page.setContent(``).catch(e => error = e); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); }); - it('should await resources to load', async({page, server}) => { + it('should await resources to load', async() => { + const { page, server } = getTestState(); + const imgPath = '/img.png'; let imgResponse = null; server.setRoute(imgPath, (req, res) => imgResponse = res); @@ -758,30 +894,42 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR imgResponse.end(); await contentPromise; }); - it('should work fast enough', async({page, server}) => { + it('should work fast enough', async() => { + const { page } = getTestState(); + for (let i = 0; i < 20; ++i) await page.setContent('
yo
'); }); - it('should work with tricky content', async({page, server}) => { + it('should work with tricky content', async() => { + const { page } = getTestState(); + await page.setContent('
hello world
' + '\x7F'); expect(await page.$eval('div', div => div.textContent)).toBe('hello world'); }); - it('should work with accents', async({page, server}) => { + it('should work with accents', async() => { + const { page } = getTestState(); + await page.setContent('
aberración
'); expect(await page.$eval('div', div => div.textContent)).toBe('aberración'); }); - it('should work with emojis', async({page, server}) => { + it('should work with emojis', async() => { + const { page } = getTestState(); + await page.setContent('
🐥
'); expect(await page.$eval('div', div => div.textContent)).toBe('🐥'); }); - it('should work with newline', async({page, server}) => { + it('should work with newline', async() => { + const { page } = getTestState(); + await page.setContent('
\n
'); expect(await page.$eval('div', div => div.textContent)).toBe('\n'); }); }); - describe_fails_ffox('Page.setBypassCSP', function() { - it('should bypass CSP meta tag', async({page, server}) => { + describeFailsFirefox('Page.setBypassCSP', function() { + it('should bypass CSP meta tag', async() => { + const { page, server } = getTestState(); + // Make sure CSP prohibits addScriptTag. await page.goto(server.PREFIX + '/csp.html'); await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e); @@ -794,7 +942,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await page.evaluate(() => window.__injected)).toBe(42); }); - it('should bypass CSP header', async({page, server}) => { + it('should bypass CSP header', async() => { + const { page, server } = getTestState(); + // Make sure CSP prohibits addScriptTag. server.setCSP('/empty.html', 'default-src "self"'); await page.goto(server.EMPTY_PAGE); @@ -808,7 +958,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(await page.evaluate(() => window.__injected)).toBe(42); }); - it('should bypass after cross-process navigation', async({page, server}) => { + it('should bypass after cross-process navigation', async() => { + const { page, server } = getTestState(); + await page.setBypassCSP(true); await page.goto(server.PREFIX + '/csp.html'); await page.addScriptTag({content: 'window.__injected = 42;'}); @@ -818,7 +970,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR await page.addScriptTag({content: 'window.__injected = 42;'}); expect(await page.evaluate(() => window.__injected)).toBe(42); }); - it('should bypass CSP in iframes as well', async({page, server}) => { + it('should bypass CSP in iframes as well', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); { // Make sure CSP prohibits addScriptTag in an iframe. @@ -840,7 +994,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.addScriptTag', function() { - it('should throw an error if no options are provided', async({page, server}) => { + it('should throw an error if no options are provided', async() => { + const { page } = getTestState(); + let error = null; try { await page.addScriptTag('/injectedfile.js'); @@ -850,34 +1006,44 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(error.message).toBe('Provide an object with a `url`, `path` or `content` property'); }); - it('should work with a url', async({page, server}) => { + it('should work with a url', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ url: '/injectedfile.js' }); expect(scriptHandle.asElement()).not.toBeNull(); expect(await page.evaluate(() => __injected)).toBe(42); }); - it('should work with a url and type=module', async({page, server}) => { + it('should work with a url and type=module', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ url: '/es6/es6import.js', type: 'module' }); expect(await page.evaluate(() => __es6injected)).toBe(42); }); - it('should work with a path and type=module', async({page, server}) => { + it('should work with a path and type=module', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ path: path.join(__dirname, 'assets/es6/es6pathimport.js'), type: 'module' }); await page.waitForFunction('window.__es6injected'); expect(await page.evaluate(() => __es6injected)).toBe(42); }); - it('should work with a content and type=module', async({page, server}) => { + it('should work with a content and type=module', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ content: `import num from '/es6/es6module.js';window.__es6injected = num;`, type: 'module' }); await page.waitForFunction('window.__es6injected'); expect(await page.evaluate(() => __es6injected)).toBe(42); }); - it('should throw an error if loading from url fail', async({page, server}) => { + it('should throw an error if loading from url fail', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); let error = null; try { @@ -888,21 +1054,27 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(error.message).toBe('Loading script from /nonexistfile.js failed'); }); - it('should work with a path', async({page, server}) => { + it('should work with a path', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); expect(scriptHandle.asElement()).not.toBeNull(); expect(await page.evaluate(() => __injected)).toBe(42); }); - it('should include sourcemap when path is provided', async({page, server}) => { + it('should include sourcemap when path is provided', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); const result = await page.evaluate(() => __injectedError.stack); expect(result).toContain(path.join('assets', 'injectedfile.js')); }); - it('should work with content', async({page, server}) => { + it('should work with content', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const scriptHandle = await page.addScriptTag({ content: 'window.__injected = 35;' }); expect(scriptHandle.asElement()).not.toBeNull(); @@ -910,14 +1082,18 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); // @see https://github.com/puppeteer/puppeteer/issues/4840 - xit('should throw when added with content to the CSP page', async({page, server}) => { + xit('should throw when added with content to the CSP page', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addScriptTag({ content: 'window.__injected = 35;' }).catch(e => error = e); expect(error).toBeTruthy(); }); - it('should throw when added with URL to the CSP page', async({page, server}) => { + it('should throw when added with URL to the CSP page', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addScriptTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedfile.js' }).catch(e => error = e); @@ -926,7 +1102,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.addStyleTag', function() { - it('should throw an error if no options are provided', async({page, server}) => { + it('should throw an error if no options are provided', async() => { + const { page } = getTestState(); + let error = null; try { await page.addStyleTag('/injectedstyle.css'); @@ -936,14 +1114,18 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(error.message).toBe('Provide an object with a `url`, `path` or `content` property'); }); - it('should work with a url', async({page, server}) => { + it('should work with a url', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const styleHandle = await page.addStyleTag({ url: '/injectedstyle.css' }); expect(styleHandle.asElement()).not.toBeNull(); expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); }); - it('should throw an error if loading from url fail', async({page, server}) => { + it('should throw an error if loading from url fail', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); let error = null; try { @@ -954,14 +1136,18 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(error.message).toBe('Loading style from /nonexistfile.js failed'); }); - it('should work with a path', async({page, server}) => { + it('should work with a path', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const styleHandle = await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); expect(styleHandle.asElement()).not.toBeNull(); expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(255, 0, 0)'); }); - it('should include sourcemap when path is provided', async({page, server}) => { + it('should include sourcemap when path is provided', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); await page.addStyleTag({ path: path.join(__dirname, 'assets/injectedstyle.css') }); const styleHandle = await page.$('style'); @@ -969,21 +1155,27 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR expect(styleContent).toContain(path.join('assets', 'injectedstyle.css')); }); - it_fails_ffox('should work with content', async({page, server}) => { + itFailsFirefox('should work with content', async() => { + const { page, server } = getTestState(); + await page.goto(server.EMPTY_PAGE); const styleHandle = await page.addStyleTag({ content: 'body { background-color: green; }' }); expect(styleHandle.asElement()).not.toBeNull(); expect(await page.evaluate(`window.getComputedStyle(document.querySelector('body')).getPropertyValue('background-color')`)).toBe('rgb(0, 128, 0)'); }); - it_fails_ffox('should throw when added with content to the CSP page', async({page, server}) => { + itFailsFirefox('should throw when added with content to the CSP page', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addStyleTag({ content: 'body { background-color: green; }' }).catch(e => error = e); expect(error).toBeTruthy(); }); - it('should throw when added with URL to the CSP page', async({page, server}) => { + it('should throw when added with URL to the CSP page', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/csp.html'); let error = null; await page.addStyleTag({ url: server.CROSS_PROCESS_PREFIX + '/injectedstyle.css' }).catch(e => error = e); @@ -992,15 +1184,19 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.url', function() { - it('should work', async({page, server}) => { + it('should work', async() => { + const { page, server } = getTestState(); + expect(page.url()).toBe('about:blank'); await page.goto(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE); }); }); - describe_fails_ffox('Page.setJavaScriptEnabled', function() { - it('should work', async({page, server}) => { + describeFailsFirefox('Page.setJavaScriptEnabled', function() { + it('should work', async() => { + const { page } = getTestState(); + await page.setJavaScriptEnabled(false); await page.goto('data:text/html, '); let error = null; @@ -1014,7 +1210,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.setCacheEnabled', function() { - it('should enable or disable the cache based on the state passed', async({page, server}) => { + it('should enable or disable the cache based on the state passed', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/cached/one-style.html'); const [cachedRequest] = await Promise.all([ server.waitForRequest('/cached/one-style.html'), @@ -1030,7 +1228,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(nonCachedRequest.headers['if-modified-since']).toBe(undefined); }); - it_fails_ffox('should stay disabled when toggling request interception on/off', async({page, server}) => { + itFailsFirefox('should stay disabled when toggling request interception on/off', async() => { + const { page, server } = getTestState(); + await page.setCacheEnabled(false); await page.setRequestInterception(true); await page.setRequestInterception(false); @@ -1044,9 +1244,13 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); }); - // Printing to pdf is currently only supported in headless - (headless ? describe_fails_ffox : xdescribe)('Page.pdf', function() { - it('should be able to save file', async({page, server}) => { + describe('printing to PDF', function() { + itFailsFirefox('can print to PDF and save to file', async() => { + // Printing to pdf is currently only supported in headless + const {isHeadless, page} = getTestState(); + + if (!isHeadless) return; + const outputFile = __dirname + '/assets/output.pdf'; await page.pdf({path: outputFile}); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); @@ -1055,26 +1259,34 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR }); describe('Page.title', function() { - it('should return the page title', async({page, server}) => { + it('should return the page title', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/title.html'); expect(await page.title()).toBe('Woof-Woof'); }); }); describe('Page.select', function() { - it('should select single option', async({page, server}) => { + it('should select single option', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue'); expect(await page.evaluate(() => result.onInput)).toEqual(['blue']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue']); }); - it('should select only first option', async({page, server}) => { + it('should select only first option', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue', 'green', 'red'); expect(await page.evaluate(() => result.onInput)).toEqual(['blue']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue']); }); - it('should not throw when select causes navigation', async({page, server}) => { + it('should not throw when select causes navigation', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/select.html'); await page.$eval('select', select => select.addEventListener('input', () => window.location = '/empty.html')); await Promise.all([ @@ -1083,60 +1295,80 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR ]); expect(page.url()).toContain('empty.html'); }); - it('should select multiple options', async({page, server}) => { + it('should select multiple options', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/select.html'); await page.evaluate(() => makeMultiple()); await page.select('select', 'blue', 'green', 'red'); expect(await page.evaluate(() => result.onInput)).toEqual(['blue', 'green', 'red']); expect(await page.evaluate(() => result.onChange)).toEqual(['blue', 'green', 'red']); }); - it('should respect event bubbling', async({page, server}) => { + it('should respect event bubbling', async() => { + const { page, server } = getTestState(); + await page.goto(server.PREFIX + '/input/select.html'); await page.select('select', 'blue'); expect(await page.evaluate(() => result.onBubblingInput)).toEqual(['blue']); expect(await page.evaluate(() => result.onBubblingChange)).toEqual(['blue']); }); - it('should throw when element is not a ', async() => { + const { page, server } = getTestState(); + let error = null; await page.goto(server.PREFIX + '/input/select.html'); await page.select('body', '').catch(e => error = e); expect(error.message).toContain('Element is not a