diff --git a/.eslintrc.js b/.eslintrc.js index a6939035c9b..49c970c4b6b 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 00000000000..32bbf7ee51d --- /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 e165913208b..2035cd26354 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 2647d2a2e36..5f16d0566ab 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') - ]); - // 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'); + const client = await page.target().createCDPSession(); - async function theSourceOfTheProblems() { - await client.send('ThisCommand.DoesNotExist'); - } - }); + 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() => { + 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'); + } + }); +}); diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000000..f69d4fe02ab --- /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 754afcd2229..68022c7807c 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 ? { - 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 function({page}) { - await page.setContent(``); - await page.focus('textarea'); - const golden = FFOX ? { - role: 'entry', - name: '', - value: 'hi', - focused: true, - multiline: true, - children: [{ - role: 'text leaf', - name: 'hi' + 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: 'textbox', - name: '', - value: 'hi', - focused: true, - multiline: true, - children: [{ - role: 'generic', - name: '', - children: [{ - role: 'text', name: 'hi' - }] + } : { + 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(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'); - }); - 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
'); - 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 function({page}) { - await page.setContent(` + }; + 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 = 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}) { - await page.setContent(` + const golden = isFirefox ? { + 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() => { + const { page, isFirefox } = 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' - }] - }; - 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(` + const golden = isFirefox ? { + 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' + }] + }; + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual(golden); + }); + 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 = FFOX ? { - role: 'entry', - name: '', - value: 'Edit this image: my fake image', - children: [{ - role: 'text', - name: '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]).toEqual(golden); + }); + + // 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 snapshot = await page.accessibility.snapshot(); + 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:' - }); - }); - it('plain text field without role should not have content', async function({page}) { - await page.setContent(` -
Edit this image:my fake image
`); - 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(` -
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'generic', - name: '' - }); + value: 'Edit this image:' }); }); - it('non editable textbox with role and tabIndex and label should not have children', async function({page}) { + 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: '' + }); + }); + 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: '' + }); + }); + }); + 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(); + + await page.setContent(``); + + const button = await page.$('button'); + expect(await page.accessibility.snapshot({root: button})).toEqual({ + role: 'button', + name: 'My Button' + }); }); + it('should work an input', 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 input = await page.$('input'); + expect(await page.accessibility.snapshot({root: input})).toEqual({ + role: 'textbox', + name: 'My Input', + value: 'My Value' }); - it('should work an input', async({page}) => { - await page.setContent(``); + }); + it('should work a menu', async() => { + const { page } = getTestState(); - 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(` + 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 61de5889afc..38f4188bb45 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 5fc0ff698c8..af243e377ee 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); - - 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); - }); - 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(); - }); - 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); - }); - 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(); - }); +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(); + + 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(); + + 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'; + }); + + 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); + }); + + 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 962a7b04b14..0aed71b26cb 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'; - - 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'); - - 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}).catch(e => error = e); - expect(error.message).toContain('Failed to fetch browser webSocket url from'); - originalBrowser.close(); - }); + 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(); - 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; - }); + const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { + args: ['--remote-debugging-port=21222'] + })); + const browserURL = 'http://127.0.0.1:21222'; + + 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'); + + 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'; + + let error = null; + await puppeteer.connect({browserURL}).catch(e => error = e); + expect(error.message).toContain('Failed to fetch browser webSocket url from'); + originalBrowser.close(); + }); }); -}; -module.exports.addPageTests = function({testRunner, expect}) { - const {describe, xdescribe, fdescribe} = testRunner; - const {it, fit, xit} = testRunner; - const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; + 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; + }); + }); - describe('Chromium-Specific Page Tests', function() { - it('Page.setRequestInterception should work with intervention headers', async({server, page}) => { - server.setRoute('/intervention', (req, res) => res.end(` +}); + +describeChromeOnly('Chromium-Specific Page Tests', function() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); + it('Page.setRequestInterception should work with intervention headers', async() => { + const { server, page } = getTestState(); + + 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 1ec8cf926e9..b90d4700fb1 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' - ]); - }); - - 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); - }); - - 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); - }); - - 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 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.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'); - }); + 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(); + + 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(); + + 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('should click wrapped links', async() => { + const { page, server } = getTestState(); + + 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; + }); + }); + 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 39ffb9bedb4..61f62e98904 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 00000000000..34dda1b5cbb --- /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 56d6b18c7ee..17ac734a4fb 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 8075ed0f08a..aa7b905ed76 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(); - }); - 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 - }]); - }); - 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 - }]); + 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 + }]); }); -}; + itFailsFirefox('page.setCookie() should work', async() => { + const { page, server } = getTestState(); + + 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 + }]); + }); + 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 1af64433e9e..06a837f9613 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')); - }); - 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!'); - }); - 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); + 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')); }); -}; + 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!'); + }); + 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 a2ba620d7e2..f18b10eb457 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 e99466ae09d..2465286f26c 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 expect = require('expect'); +const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils'); - const iPhone = puppeteer.devices['iPhone 6']; - const iPhoneLandscape = puppeteer.devices['iPhone 6 landscape']; +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 545452d6eac..f2c0896d1cf 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 102ec49e168..57daaeaba05 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://'); - }); - 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); - }); + 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, + }); + 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 fac89dea322..937395e239a 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 e1434aafcda..aa2a95fe6e4 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 3ca3c072614..57796083c46 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'); - 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); - }); - }); +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; - 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); + 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(); + }); + + 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 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('should be |null| for non-secure requests', async() => { + const { server } = getTestState(); + + const response = await page.goto(server.EMPTY_PAGE); + expect(response.securityDetails()).toBe(null); }); - 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('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', 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 39ef35ab1fa..4418fa58451 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 ac13b5d8fc4..5f11172a56f 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 1347514b04d..ffe0232890c 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() { + setupTestBrowserHooks(); + setupTestPageAndContextHooks(); - 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'); - - await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); - - 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] + ' []'); - } - }); - 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); - }); - 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 textarea.press('Digit5'); - expect(await page.evaluate('keyLocation')).toBe(0); - - 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({page, server}) => { - 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: "😊"'); - }); - 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 🇯🇵'); - }); - 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 🇯🇵'); - }); - 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'); - - if (FFOX) - expect(code).toBe('OSLeft'); - else - expect(code).toBe('MetaLeft'); - - if (FFOX && os.platform() !== 'darwin') - expect(metaKey).toBe(false); - else - expect(metaKey).toBe(true); + it('should type into a textarea', async() => { + const { page } = getTestState(); + 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); }); -}; + 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))); + }); + 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 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 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 page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true)); + + 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(); + + 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(); + + 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); + }); + 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); + }); + const textarea = await page.$('textarea'); + + await textarea.press('Digit5'); + expect(await page.evaluate('keyLocation')).toBe(0); + + 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 4642e67744b..1a32fe182cd 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 00000000000..6e9dadbb384 --- /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 57d8fd30e9a..21815838f7f 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.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]); - }); - }); - 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.click(30, 40); - - expect(await page.evaluate('result')).toEqual({x: 30, y: 40}); }); + 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'); + 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 + 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); + + expect(await page.evaluate('result')).toEqual({x: 30, y: 40}); + }); +}); diff --git a/test/navigation.spec.js b/test/navigation.spec.js index 934b397651a..c00675baf06 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 bb19f0299e4..053a82e0889 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 f3a4c3391c0..5f7f940d370 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; +const expect = require('expect'); +const {getTestState} = require('./mocha-utils'); - 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); - }); +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 908799f45a1..bfcb702d96a 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