chore: migrate unit tests to Mocha (#5600)

Rather than maintain our own test runner we should instead lean on the community and use Mocha which is very popular and also our test runner of choice in DevTools too.

Note that this commit doesn't remove the TestRunner source as it's still used for other unit tests, but they will be updated in a future PR and then we can remove the TestRunner.

The main bulk of this PR is updating the tests as the old TestRunner passed in contextual data via the `it` function callback whereas Mocha does not, so we introduce some helpers for the tests to make it easier.
This commit is contained in:
Jack Franklin 2020-04-09 06:56:25 +01:00 committed by GitHub
parent 262da92bbb
commit 17cd8703f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 4159 additions and 2812 deletions

View File

@ -10,6 +10,10 @@ module.exports = {
"ecmaVersion": 9 "ecmaVersion": 9
}, },
"plugins": [
"mocha"
],
/** /**
* ESLint rules * ESLint rules
* *
@ -107,6 +111,9 @@ module.exports = {
"indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }], "indent": [2, 2, { "SwitchCase": 1, "CallExpression": {"arguments": 2}, "MemberExpression": 2 }],
"key-spacing": [2, { "key-spacing": [2, {
"beforeColon": false "beforeColon": false
}] }],
// ensure we don't have any it.only or describe.only in prod
"mocha/no-exclusive-tests": "error"
} }
}; };

6
.mocharc.js Normal file
View File

@ -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,
};

View File

@ -12,8 +12,9 @@
"firefox_revision": "latest" "firefox_revision": "latest"
}, },
"scripts": { "scripts": {
"unit": "node test/test.js", "unit": "mocha --config .mocharc.js",
"funit": "PUPPETEER_PRODUCT=firefox node test/test.js", "coverage": "cross-env COVERAGE=1 npm run unit",
"funit": "PUPPETEER_PRODUCT=firefox npm run unit",
"debug-unit": "node --inspect-brk test/test.js", "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-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", "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", "install": "node install.js",
"lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc", "lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc",
"doc": "node utils/doclint/cli.js", "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/", "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", "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", "bundle": "npm run tsc && npx browserify -r ./index.js:puppeteer -o utils/browser/puppeteer-web.js",
@ -57,10 +57,12 @@
"commonmark": "^0.28.1", "commonmark": "^0.28.1",
"cross-env": "^5.0.5", "cross-env": "^5.0.5",
"eslint": "^6.8.0", "eslint": "^6.8.0",
"eslint-plugin-mocha": "^6.3.0",
"esprima": "^4.0.0", "esprima": "^4.0.0",
"expect": "^25.2.7", "expect": "^25.2.7",
"jpeg-js": "^0.3.4", "jpeg-js": "^0.3.4",
"minimist": "^1.2.0", "minimist": "^1.2.0",
"mocha": "^7.1.1",
"ncp": "^2.0.0", "ncp": "^2.0.0",
"pixelmatch": "^4.0.2", "pixelmatch": "^4.0.2",
"pngjs": "^3.3.3", "pngjs": "^3.3.3",

View File

@ -15,69 +15,78 @@
*/ */
const {waitEvent} = require('./utils'); const {waitEvent} = require('./utils');
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) { describeChromeOnly('Target.createCDPSession', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Target.createCDPSession', function() { it('should work', async() => {
it('should work', async function({page, server}) { const { page } = getTestState();
const client = await page.target().createCDPSession();
await Promise.all([ const client = await page.target().createCDPSession();
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');
async function theSourceOfTheProblems() { await Promise.all([
await client.send('ThisCommand.DoesNotExist'); 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');
}
});
});

34
test/README.md Normal file
View File

@ -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

View File

@ -14,14 +14,17 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, FFOX}) { const expect = require('expect');
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe_fails_ffox('Accessibility', function() { describeFailsFirefox('Accessibility', function() {
it('should work', async function({page}) { setupTestBrowserHooks();
await page.setContent(` setupTestPageAndContextHooks();
it('should work', async() => {
const { page, isFirefox } = getTestState();
await page.setContent(`
<head> <head>
<title>Accessibility Test</title> <title>Accessibility Test</title>
</head> </head>
@ -42,296 +45,333 @@ module.exports.addTests = function({testRunner, expect, FFOX}) {
</select> </select>
</body>`); </body>`);
await page.focus('[placeholder="Empty input"]'); await page.focus('[placeholder="Empty input"]');
const golden = FFOX ? { const golden = isFirefox ? {
role: 'document', role: 'document',
name: 'Accessibility Test', name: 'Accessibility Test',
children: [ children: [
{role: 'text leaf', name: 'Hello World'}, {role: 'text leaf', name: 'Hello World'},
{role: 'heading', name: 'Inputs', level: 1}, {role: 'heading', name: 'Inputs', level: 1},
{role: 'entry', name: 'Empty input', focused: true}, {role: 'entry', name: 'Empty input', focused: true},
{role: 'entry', name: 'readonly input', readonly: true}, {role: 'entry', name: 'readonly input', readonly: true},
{role: 'entry', name: 'disabled input', disabled: true}, {role: 'entry', name: 'disabled input', disabled: true},
{role: 'entry', name: 'Input with whitespace', value: ' '}, {role: 'entry', name: 'Input with whitespace', value: ' '},
{role: 'entry', name: '', value: 'value only'}, {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'}, // firefox doesn't use aria-placeholder for the name
{role: 'entry', name: '', value: 'and a value', description: 'This is a description!'}, // and here {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', name: '', value: 'First Option', haspopup: true, children: [
{role: 'combobox option', name: 'First Option', selected: true}, {role: 'combobox option', name: 'First Option', selected: true},
{role: 'combobox option', name: 'Second Option'}] {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(`<textarea>hi</textarea>`);
await page.focus('textarea');
const golden = FFOX ? {
role: 'entry',
name: '',
value: 'hi',
focused: true,
multiline: true,
children: [{
role: 'text leaf',
name: 'hi'
}] }]
} : { } : {
role: 'textbox', role: 'WebArea',
name: '', name: 'Accessibility Test',
value: 'hi', children: [
focused: true, {role: 'text', name: 'Hello World'},
multiline: true, {role: 'heading', name: 'Inputs', level: 1},
children: [{ {role: 'textbox', name: 'Empty input', focused: true},
role: 'generic', {role: 'textbox', name: 'readonly input', readonly: true},
name: '', {role: 'textbox', name: 'disabled input', disabled: true},
children: [{ {role: 'textbox', name: 'Input with whitespace', value: ' '},
role: 'text', name: 'hi' {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); expect(await page.accessibility.snapshot()).toEqual(golden);
}); });
it('roledescription', async({page}) => { it('should report uninteresting nodes', async() => {
await page.setContent('<div tabIndex=-1 aria-roledescription="foo">Hi</div>'); const { page, isFirefox } = getTestState();
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0].roledescription).toEqual('foo'); await page.setContent(`<textarea>hi</textarea>`);
}); await page.focus('textarea');
it('orientation', async({page}) => { const golden = isFirefox ? {
await page.setContent('<a href="" role="slider" aria-orientation="vertical">11</a>'); role: 'entry',
const snapshot = await page.accessibility.snapshot(); name: '',
expect(snapshot.children[0].orientation).toEqual('vertical'); value: 'hi',
}); focused: true,
it('autocomplete', async({page}) => { multiline: true,
await page.setContent('<input type="number" aria-autocomplete="list" />'); children: [{
const snapshot = await page.accessibility.snapshot(); role: 'text leaf',
expect(snapshot.children[0].autocomplete).toEqual('list'); name: 'hi'
}); }]
it('multiselectable', async({page}) => { } : {
await page.setContent('<div role="grid" tabIndex=-1 aria-multiselectable=true>hey</div>'); role: 'textbox',
const snapshot = await page.accessibility.snapshot(); name: '',
expect(snapshot.children[0].multiselectable).toEqual(true); value: 'hi',
}); focused: true,
it('keyshortcuts', async({page}) => { multiline: true,
await page.setContent('<div role="grid" tabIndex=-1 aria-keyshortcuts="foo">hey</div>'); children: [{
const snapshot = await page.accessibility.snapshot(); role: 'generic',
expect(snapshot.children[0].keyshortcuts).toEqual('foo'); name: '',
}); children: [{
describe('filtering children of leaf nodes', function() { role: 'text', name: 'hi'
it('should not report text nodes inside controls', async function({page}) { }]
await page.setContent(` }]
};
expect(findFocusedNode(await page.accessibility.snapshot({interestingOnly: false}))).toEqual(golden);
});
it('roledescription', async() => {
const { page } = getTestState();
await page.setContent('<div tabIndex=-1 aria-roledescription="foo">Hi</div>');
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0].roledescription).toEqual('foo');
});
it('orientation', async() => {
const { page } = getTestState();
await page.setContent('<a href="" role="slider" aria-orientation="vertical">11</a>');
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0].orientation).toEqual('vertical');
});
it('autocomplete', async() => {
const { page } = getTestState();
await page.setContent('<input type="number" aria-autocomplete="list" />');
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0].autocomplete).toEqual('list');
});
it('multiselectable', async() => {
const { page } = getTestState();
await page.setContent('<div role="grid" tabIndex=-1 aria-multiselectable=true>hey</div>');
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0].multiselectable).toEqual(true);
});
it('keyshortcuts', async() => {
const { page } = getTestState();
await page.setContent('<div role="grid" tabIndex=-1 aria-keyshortcuts="foo">hey</div>');
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(`
<div role="tablist"> <div role="tablist">
<div role="tab" aria-selected="true"><b>Tab1</b></div> <div role="tab" aria-selected="true"><b>Tab1</b></div>
<div role="tab">Tab2</div> <div role="tab">Tab2</div>
</div>`); </div>`);
const golden = FFOX ? { const golden = isFirefox ? {
role: 'document', role: 'document',
name: '', name: '',
children: [{ children: [{
role: 'pagetab', role: 'pagetab',
name: 'Tab1', name: 'Tab1',
selected: true selected: true
}, { }, {
role: 'pagetab', role: 'pagetab',
name: 'Tab2' name: 'Tab2'
}] }]
} : { } : {
role: 'WebArea', role: 'WebArea',
name: '', name: '',
children: [{ children: [{
role: 'tab', role: 'tab',
name: 'Tab1', name: 'Tab1',
selected: true selected: true
}, { }, {
role: 'tab', role: 'tab',
name: 'Tab2' name: 'Tab2'
}] }]
}; };
expect(await page.accessibility.snapshot()).toEqual(golden); expect(await page.accessibility.snapshot()).toEqual(golden);
}); });
it('rich text editable fields should have children', async function({page}) { it('rich text editable fields should have children', async() => {
await page.setContent(` const { page, isFirefox } = getTestState();
await page.setContent(`
<div contenteditable="true"> <div contenteditable="true">
Edit this image: <img src="fakeimage.png" alt="my fake image"> Edit this image: <img src="fakeimage.png" alt="my fake image">
</div>`); </div>`);
const golden = FFOX ? { const golden = isFirefox ? {
role: 'section', role: 'section',
name: '', name: '',
children: [{ children: [{
role: 'text leaf', role: 'text leaf',
name: 'Edit this image: ' name: 'Edit this image: '
}, { }, {
role: 'text', role: 'text',
name: 'my fake image' name: 'my fake image'
}] }]
} : { } : {
role: 'generic', role: 'generic',
name: '', name: '',
value: 'Edit this image: ', value: 'Edit this image: ',
children: [{ children: [{
role: 'text', role: 'text',
name: 'Edit this image:' name: 'Edit this image:'
}, { }, {
role: 'img', role: 'img',
name: 'my fake image' name: 'my fake image'
}] }]
}; };
const snapshot = await page.accessibility.snapshot(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual(golden); expect(snapshot.children[0]).toEqual(golden);
}); });
it('rich text editable fields with role should have children', async function({page}) { it('rich text editable fields with role should have children', async() => {
await page.setContent(` const { page, isFirefox } = getTestState();
await page.setContent(`
<div contenteditable="true" role='textbox'> <div contenteditable="true" role='textbox'>
Edit this image: <img src="fakeimage.png" alt="my fake image"> Edit this image: <img src="fakeimage.png" alt="my fake image">
</div>`); </div>`);
const golden = FFOX ? { const golden = isFirefox ? {
role: 'entry', role: 'entry',
name: '', name: '',
value: 'Edit this image: my fake image', value: 'Edit this image: my fake image',
children: [{ children: [{
role: 'text', role: 'text',
name: 'my fake image' 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(`
<div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual({
role: 'textbox', role: 'textbox',
name: '', name: '',
value: 'Edit this image: ', 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(`
<div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
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(`
<div contenteditable="plaintext-only">Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
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(`
<div contenteditable="plaintext-only" tabIndex=0>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual({
role: 'generic',
name: ''
});
}); });
}); });
it('non editable textbox with role and tabIndex and label should not have children', async function({page}) { it('plain text field without role should not have content', async() => {
const { page } = getTestState();
await page.setContent(` await page.setContent(`
<div contenteditable="plaintext-only">Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
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(`
<div contenteditable="plaintext-only" tabIndex=0>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
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(`
<div role="textbox" tabIndex=0 aria-checked="true" aria-label="my favorite textbox"> <div role="textbox" tabIndex=0 aria-checked="true" aria-label="my favorite textbox">
this is the inner content this is the inner content
<img alt="yo" src="fakeimg.png"> <img alt="yo" src="fakeimg.png">
</div>`); </div>`);
const golden = FFOX ? { const golden = isFirefox ? {
role: 'entry', role: 'entry',
name: 'my favorite textbox', name: 'my favorite textbox',
value: 'this is the inner content yo' value: 'this is the inner content yo'
} : { } : {
role: 'textbox', role: 'textbox',
name: 'my favorite textbox', name: 'my favorite textbox',
value: 'this is the inner content ' value: 'this is the inner content '
}; };
const snapshot = await page.accessibility.snapshot(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual(golden); expect(snapshot.children[0]).toEqual(golden);
}); });
it('checkbox with and tabIndex and label should not have children', async function({page}) { it('checkbox with and tabIndex and label should not have children', async() => {
await page.setContent(` const { page, isFirefox } = getTestState();
await page.setContent(`
<div role="checkbox" tabIndex=0 aria-checked="true" aria-label="my favorite checkbox"> <div role="checkbox" tabIndex=0 aria-checked="true" aria-label="my favorite checkbox">
this is the inner content this is the inner content
<img alt="yo" src="fakeimg.png"> <img alt="yo" src="fakeimg.png">
</div>`); </div>`);
const golden = FFOX ? { const golden = isFirefox ? {
role: 'checkbutton', role: 'checkbutton',
name: 'my favorite checkbox', name: 'my favorite checkbox',
checked: true checked: true
} : { } : {
role: 'checkbox', role: 'checkbox',
name: 'my favorite checkbox', name: 'my favorite checkbox',
checked: true checked: true
}; };
const snapshot = await page.accessibility.snapshot(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual(golden); expect(snapshot.children[0]).toEqual(golden);
}); });
it('checkbox without label should not have children', async function({page}) { it('checkbox without label should not have children', async() => {
await page.setContent(` const { page, isFirefox } = getTestState();
await page.setContent(`
<div role="checkbox" aria-checked="true"> <div role="checkbox" aria-checked="true">
this is the inner content this is the inner content
<img alt="yo" src="fakeimg.png"> <img alt="yo" src="fakeimg.png">
</div>`); </div>`);
const golden = FFOX ? { const golden = isFirefox ? {
role: 'checkbutton', role: 'checkbutton',
name: 'this is the inner content yo', name: 'this is the inner content yo',
checked: true checked: true
} : { } : {
role: 'checkbox', role: 'checkbox',
name: 'this is the inner content yo', name: 'this is the inner content yo',
checked: true checked: true
}; };
const snapshot = await page.accessibility.snapshot(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual(golden); expect(snapshot.children[0]).toEqual(golden);
});
describe('root option', function() {
it('should work a button', async() => {
const { page } = getTestState();
await page.setContent(`<button>My Button</button>`);
const button = await page.$('button');
expect(await page.accessibility.snapshot({root: button})).toEqual({
role: 'button',
name: 'My Button'
});
}); });
it('should work an input', async() => {
const { page } = getTestState();
describe('root option', function() { await page.setContent(`<input title="My Input" value="My Value">`);
it('should work a button', async({page}) => {
await page.setContent(`<button>My Button</button>`);
const button = await page.$('button'); const input = await page.$('input');
expect(await page.accessibility.snapshot({root: button})).toEqual({ expect(await page.accessibility.snapshot({root: input})).toEqual({
role: 'button', role: 'textbox',
name: 'My Button' name: 'My Input',
}); value: 'My Value'
}); });
it('should work an input', async({page}) => { });
await page.setContent(`<input title="My Input" value="My Value">`); it('should work a menu', async() => {
const { page } = getTestState();
const input = await page.$('input'); await page.setContent(`
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(`
<div role="menu" title="My Menu"> <div role="menu" title="My Menu">
<div role="menuitem">First Item</div> <div role="menuitem">First Item</div>
<div role="menuitem">Second Item</div> <div role="menuitem">Second Item</div>
@ -339,51 +379,54 @@ module.exports.addTests = function({testRunner, expect, FFOX}) {
</div> </div>
`); `);
const menu = await page.$('div[role="menu"]'); const menu = await page.$('div[role="menu"]');
expect(await page.accessibility.snapshot({root: menu})).toEqual({ expect(await page.accessibility.snapshot({root: menu})).toEqual({
role: 'menu', role: 'menu',
name: 'My Menu', name: 'My Menu',
children: children:
[ { role: 'menuitem', name: 'First Item' }, [ { role: 'menuitem', name: 'First Item' },
{ role: 'menuitem', name: 'Second Item' }, { role: 'menuitem', name: 'Second Item' },
{ role: 'menuitem', name: 'Third Item' } ] { role: 'menuitem', name: 'Third Item' } ]
});
}); });
it('should return null when the element is no longer in DOM', async({page}) => { });
await page.setContent(`<button>My Button</button>`); it('should return null when the element is no longer in DOM', async() => {
const button = await page.$('button'); const { page } = getTestState();
await page.$eval('button', button => button.remove());
expect(await page.accessibility.snapshot({root: button})).toEqual(null); await page.setContent(`<button>My Button</button>`);
}); const button = await page.$('button');
it('should support the interestingOnly option', async({page}) => { await page.$eval('button', button => button.remove());
await page.setContent(`<div><button>My Button</button></div>`); expect(await page.accessibility.snapshot({root: button})).toEqual(null);
const div = await page.$('div'); });
expect(await page.accessibility.snapshot({root: div})).toEqual(null); it('should support the interestingOnly option', async() => {
expect(await page.accessibility.snapshot({root: div, interestingOnly: false})).toEqual({ const { page } = getTestState();
role: 'generic',
name: '', await page.setContent(`<div><button>My Button</button></div>`);
children: [ const div = await page.$('div');
{ expect(await page.accessibility.snapshot({root: div})).toEqual(null);
role: 'button', expect(await page.accessibility.snapshot({root: div, interestingOnly: false})).toEqual({
name: 'My Button', role: 'generic',
children: [ name: '',
{ role: 'text', name: 'My Button' }, 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;
}
});

View File

@ -14,24 +14,29 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHROME}) { const expect = require('expect');
const {describe, xdescribe, fdescribe} = testRunner; const {getTestState, setupTestBrowserHooks} = require('./mocha-utils');
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describe('Browser specs', function() {
setupTestBrowserHooks();
describe('Browser.version', function() { 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(); const version = await browser.version();
expect(version.length).toBeGreaterThan(0); expect(version.length).toBeGreaterThan(0);
expect(version.startsWith('Headless')).toBe(headless); expect(version.startsWith('Headless')).toBe(isHeadless);
}); });
}); });
describe('Browser.userAgent', function() { describe('Browser.userAgent', function() {
it('should include WebKit', async({browser}) => { it('should include WebKit', async() => {
const { browser, isChrome } = getTestState();
const userAgent = await browser.userAgent(); const userAgent = await browser.userAgent();
expect(userAgent.length).toBeGreaterThan(0); expect(userAgent.length).toBeGreaterThan(0);
if (CHROME) if (isChrome)
expect(userAgent).toContain('WebKit'); expect(userAgent).toContain('WebKit');
else else
expect(userAgent).toContain('Gecko'); expect(userAgent).toContain('Gecko');
@ -39,18 +44,24 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR
}); });
describe('Browser.target', function() { describe('Browser.target', function() {
it('should return browser target', async({browser}) => { it('should return browser target', async() => {
const { browser } = getTestState();
const target = browser.target(); const target = browser.target();
expect(target.type()).toBe('browser'); expect(target.type()).toBe('browser');
}); });
}); });
describe('Browser.process', function() { 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(); const process = await browser.process();
expect(process.pid).toBeGreaterThan(0); 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 browserWSEndpoint = browser.wsEndpoint();
const remoteBrowser = await puppeteer.connect({browserWSEndpoint}); const remoteBrowser = await puppeteer.connect({browserWSEndpoint});
expect(remoteBrowser.process()).toBe(null); expect(remoteBrowser.process()).toBe(null);
@ -59,7 +70,9 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR
}); });
describe('Browser.isConnected', () => { 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 browserWSEndpoint = browser.wsEndpoint();
const newBrowser = await puppeteer.connect({browserWSEndpoint}); const newBrowser = await puppeteer.connect({browserWSEndpoint});
expect(newBrowser.isConnected()).toBe(true); expect(newBrowser.isConnected()).toBe(true);
@ -67,4 +80,4 @@ module.exports.addTests = function({testRunner, expect, headless, puppeteer, CHR
expect(newBrowser.isConnected()).toBe(false); expect(newBrowser.isConnected()).toBe(false);
}); });
}); });
}; });

View File

@ -14,143 +14,160 @@
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState, setupTestBrowserHooks} = require('./mocha-utils');
const utils = require('./utils'); const utils = require('./utils');
module.exports.addTests = function({testRunner, expect, puppeteer}) { describe('BrowserContext', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; itFailsFirefox('should have default context', async() => {
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const { browser } = getTestState();
expect(browser.browserContexts().length).toEqual(1);
describe('BrowserContext', function() { const defaultContext = browser.browserContexts()[0];
it_fails_ffox('should have default context', async function({browser, server}) { expect(defaultContext.isIncognito()).toBe(false);
expect(browser.browserContexts().length).toBe(1); let error = null;
const defaultContext = browser.browserContexts()[0]; await defaultContext.close().catch(e => error = e);
expect(defaultContext.isIncognito()).toBe(false); expect(browser.defaultBrowserContext()).toBe(defaultContext);
let error = null; expect(error.message).toContain('cannot be closed');
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();
});
}); });
}; 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();
});
});

View File

@ -13,116 +13,118 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addLauncherTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { describeChromeOnly('Chromium-Specific Launcher tests', function() {
const {describe, xdescribe, fdescribe} = testRunner; describe('Puppeteer.launch |browserURL| option', function() {
const {it, fit, xit} = testRunner; it('should be able to connect using browserUrl, with and without trailing slash', async() => {
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const {defaultBrowserOptions, puppeteer} = getTestState();
describe('Chromium-Specific Launcher tests', function() { const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
describe('Puppeteer.launch |browserURL| option', function() { args: ['--remote-debugging-port=21222']
it('should be able to connect using browserUrl, with and without trailing slash', async({server}) => { }));
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { const browserURL = 'http://127.0.0.1:21222';
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:21222';
const browser1 = await puppeteer.connect({browserURL}); const browser1 = await puppeteer.connect({browserURL});
const page1 = await browser1.newPage(); const page1 = await browser1.newPage();
expect(await page1.evaluate(() => 7 * 8)).toBe(56); expect(await page1.evaluate(() => 7 * 8)).toBe(56);
browser1.disconnect(); browser1.disconnect();
const browser2 = await puppeteer.connect({browserURL: browserURL + '/'}); const browser2 = await puppeteer.connect({browserURL: browserURL + '/'});
const page2 = await browser2.newPage(); const page2 = await browser2.newPage();
expect(await page2.evaluate(() => 8 * 7)).toBe(56); expect(await page2.evaluate(() => 8 * 7)).toBe(56);
browser2.disconnect(); browser2.disconnect();
originalBrowser.close(); 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();
});
}); });
it('should throw when using both browserWSEndpoint and browserURL', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
describe('Puppeteer.launch |pipe| option', function() { const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
it('should support the pipe option', async() => { args: ['--remote-debugging-port=21222']
const options = Object.assign({pipe: true}, defaultBrowserOptions); }));
const browser = await puppeteer.launch(options); const browserURL = 'http://127.0.0.1:21222';
expect((await browser.pages()).length).toBe(1);
expect(browser.wsEndpoint()).toBe(''); let error = null;
const page = await browser.newPage(); await puppeteer.connect({browserURL, browserWSEndpoint: originalBrowser.wsEndpoint()}).catch(e => error = e);
expect(await page.evaluate('11 * 11')).toBe(121); expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport');
await page.close();
await browser.close(); originalBrowser.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;
});
}); });
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}) { describe('Puppeteer.launch |pipe| option', function() {
const {describe, xdescribe, fdescribe} = testRunner; it('should support the pipe option', async() => {
const {it, fit, xit} = testRunner; const {defaultBrowserOptions, puppeteer} = getTestState();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; 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(`
<script> <script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>'); document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
</script> </script>
`)); `));
server.setRedirect('/intervention.js', '/redirect.js'); server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null; let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => { server.setRoute('/redirect.js', (req, res) => {
serverRequest = req; serverRequest = req;
res.end('console.log(1);'); 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');
}); });
});
};
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');
});
});

View File

@ -14,37 +14,44 @@
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState,setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils');
const utils = require('./utils'); const utils = require('./utils');
module.exports.addTests = function({testRunner, expect, puppeteer}) { describe('Page.click', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; it('should click the button', async() => {
const { page, server } = getTestState();
describe('Page.click', function() { await page.goto(server.PREFIX + '/input/button.html');
it('should click the button', async({page, server}) => { await page.click('button');
await page.goto(server.PREFIX + '/input/button.html'); expect(await page.evaluate(() => result)).toBe('Clicked');
await page.click('button'); });
expect(await page.evaluate(() => result)).toBe('Clicked'); itFailsFirefox('should click svg', async() => {
}); const { page } = getTestState();
it_fails_ffox('should click svg', async({page, server}) => {
await page.setContent(` await page.setContent(`
<svg height="100" width="100"> <svg height="100" width="100">
<circle onclick="javascript:window.__CLICKED=42" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" /> <circle onclick="javascript:window.__CLICKED=42" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg> </svg>
`); `);
await page.click('circle'); await page.click('circle');
expect(await page.evaluate(() => window.__CLICKED)).toBe(42); expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
}); });
it_fails_ffox('should click the button if window.Node is removed', async({page, server}) => { itFailsFirefox('should click the button if window.Node is removed', async() => {
await page.goto(server.PREFIX + '/input/button.html'); const { page, server } = getTestState();
await page.evaluate(() => delete window.Node);
await page.click('button'); await page.goto(server.PREFIX + '/input/button.html');
expect(await page.evaluate(() => result)).toBe('Clicked'); await page.evaluate(() => delete window.Node);
}); await page.click('button');
// @see https://github.com/puppeteer/puppeteer/issues/4281 expect(await page.evaluate(() => result)).toBe('Clicked');
it_fails_ffox('should click on a span with an inline element inside', async({page, server}) => { });
await page.setContent(` // @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(`
<style> <style>
span::before { span::before {
content: 'q'; content: 'q';
@ -52,34 +59,42 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
</style> </style>
<span onclick='javascript:window.CLICKED=42'></span> <span onclick='javascript:window.CLICKED=42'></span>
`); `);
await page.click('span'); await page.click('span');
expect(await page.evaluate(() => window.CLICKED)).toBe(42); expect(await page.evaluate(() => window.CLICKED)).toBe(42);
}); });
it('should not throw UnhandledPromiseRejection when page closes', async({page, server}) => { it('should not throw UnhandledPromiseRejection when page closes', async() => {
const newPage = await page.browser().newPage(); const { page } = getTestState();
await Promise.all([
newPage.close(), const newPage = await page.browser().newPage();
newPage.mouse.click(1, 2), await Promise.all([
]).catch(e => {}); newPage.close(),
}); newPage.mouse.click(1, 2),
it('should click the button after navigation ', async({page, server}) => { ]).catch(e => {});
await page.goto(server.PREFIX + '/input/button.html'); });
await page.click('button'); it('should click the button after navigation ', async() => {
await page.goto(server.PREFIX + '/input/button.html'); const { page, server } = getTestState();
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked'); await page.goto(server.PREFIX + '/input/button.html');
}); await page.click('button');
it_fails_ffox('should click with disabled javascript', async({page, server}) => { await page.goto(server.PREFIX + '/input/button.html');
await page.setJavaScriptEnabled(false); await page.click('button');
await page.goto(server.PREFIX + '/wrappedlink.html'); expect(await page.evaluate(() => result)).toBe('Clicked');
await Promise.all([ });
page.click('a'), itFailsFirefox('should click with disabled javascript', async() => {
page.waitForNavigation() const { page, server } = getTestState();
]);
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked'); await page.setJavaScriptEnabled(false);
}); await page.goto(server.PREFIX + '/wrappedlink.html');
it_fails_ffox('should click when one of inline box children is outside of viewport', async({page, server}) => { await Promise.all([
await page.setContent(` 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(`
<style> <style>
i { i {
position: absolute; position: absolute;
@ -88,175 +103,206 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
</style> </style>
<span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span> <span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span>
`); `);
await page.click('span'); await page.click('span');
expect(await page.evaluate(() => window.CLICKED)).toBe(42); 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(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
// 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('<div style="width:100px;height:100px">spacer</div>');
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('<div style="width:100px;height:2000px">spacer</div>');
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('<div style="width:100px;height:100px">spacer</div>');
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');
});
}); });
}; 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(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
// 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('<div style="width:100px;height:100px">spacer</div>');
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('<div style="width:100px;height:2000px">spacer</div>');
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('<div style="width:100px;height:100px">spacer</div>');
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');
});
});

View File

@ -13,18 +13,21 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) { describe('Cookie specs', () => {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.cookies', function() { 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); await page.goto(server.EMPTY_PAGE);
expect(await page.cookies()).toEqual([]); 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
document.cookie = 'username=John Doe'; document.cookie = 'username=John Doe';
@ -41,7 +44,8 @@ module.exports.addTests = function({testRunner, expect}) {
session: true 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) => { server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', ';HttpOnly; Path=/'); res.setHeader('Set-Cookie', ';HttpOnly; Path=/');
res.end(); res.end();
@ -51,7 +55,8 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies.length).toBe(1); expect(cookies.length).toBe(1);
expect(cookies[0].httpOnly).toBe(true); 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) => { server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', ';SameSite=Strict'); res.setHeader('Set-Cookie', ';SameSite=Strict');
res.end(); res.end();
@ -61,7 +66,8 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies.length).toBe(1); expect(cookies.length).toBe(1);
expect(cookies[0].sameSite).toBe('Strict'); 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) => { server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', ';SameSite=Lax'); res.setHeader('Set-Cookie', ';SameSite=Lax');
res.end(); res.end();
@ -71,7 +77,8 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies.length).toBe(1); expect(cookies.length).toBe(1);
expect(cookies[0].sameSite).toBe('Lax'); 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
document.cookie = 'username=John Doe'; 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({ await page.setCookie({
url: 'https://foo.com', url: 'https://foo.com',
name: 'doggo', name: 'doggo',
@ -143,9 +151,10 @@ module.exports.addTests = function({testRunner, expect}) {
}]); }]);
}); });
}); });
describe('Page.setCookie', function() { 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.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.setCookie({
name: 'password', name: 'password',
@ -153,7 +162,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
expect(await page.evaluate(() => document.cookie)).toEqual('password=123456'); 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 anotherContext = await browser.createIncognitoBrowserContext();
const anotherPage = await anotherContext.newPage(); const anotherPage = await anotherContext.newPage();
@ -173,7 +184,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies2[0].value).toBe('page2value'); expect(cookies2[0].value).toBe('page2value');
await anotherContext.close(); 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.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.setCookie({
name: 'password', name: 'password',
@ -190,7 +203,9 @@ module.exports.addTests = function({testRunner, expect}) {
'password=123456', '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.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.setCookie({
name: 'password', name: 'password',
@ -200,7 +215,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies[0].session).toBe(true); expect(cookies[0].session).toBe(true);
expect(cookies[0].expires).toBe(-1); 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.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.setCookie({
name: 'password', name: 'password',
@ -219,7 +236,9 @@ module.exports.addTests = function({testRunner, expect}) {
session: true 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.goto(server.PREFIX + '/grid.html');
await page.setCookie({ await page.setCookie({
name: 'gridcookie', name: 'gridcookie',
@ -244,7 +263,9 @@ module.exports.addTests = function({testRunner, expect}) {
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID'); 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'); await page.goto('about:blank');
let error = null; let error = null;
try { 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'); 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; let error = null;
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
try { try {
@ -269,7 +292,9 @@ module.exports.addTests = function({testRunner, expect}) {
`Blank page can not have cookie "example-cookie-blank"` `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; let error = null;
await page.goto('data:,Hello%2C%20World!'); await page.goto('data:,Hello%2C%20World!');
try { 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'); 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); await page.goto(server.EMPTY_PAGE);
const SECURE_URL = 'https://example.com'; const SECURE_URL = 'https://example.com';
await page.setCookie({ await page.setCookie({
@ -290,7 +317,9 @@ module.exports.addTests = function({testRunner, expect}) {
const [cookie] = await page.cookies(SECURE_URL); const [cookie] = await page.cookies(SECURE_URL);
expect(cookie.secure).toBe(true); 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); await page.goto(server.EMPTY_PAGE);
const HTTP_URL = 'http://example.com'; const HTTP_URL = 'http://example.com';
await page.setCookie({ await page.setCookie({
@ -301,7 +330,9 @@ module.exports.addTests = function({testRunner, expect}) {
const [cookie] = await page.cookies(HTTP_URL); const [cookie] = await page.cookies(HTTP_URL);
expect(cookie.secure).toBe(false); 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.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.setCookie({
url: 'https://www.example.com', url: 'https://www.example.com',
@ -322,7 +353,9 @@ module.exports.addTests = function({testRunner, expect}) {
session: true 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.goto(server.PREFIX + '/grid.html');
await page.setCookie({name: 'localhost-cookie', value: 'best'}); await page.setCookie({name: 'localhost-cookie', value: 'best'});
await page.evaluate(src => { await page.evaluate(src => {
@ -365,7 +398,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
describe('Page.deleteCookie', function() { 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.goto(server.EMPTY_PAGE);
await page.setCookie({ await page.setCookie({
name: 'cookie1', name: 'cookie1',
@ -382,4 +417,4 @@ module.exports.addTests = function({testRunner, expect}) {
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3'); expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3');
}); });
}); });
}; });

84
test/coverage-utils.js Normal file
View File

@ -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<string, boolean>} 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');
});
};

View File

@ -14,13 +14,16 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect}) { const expect = require('expect');
const {describe, xdescribe, fdescribe} = testRunner; const {getTestState, setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils');
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('JSCoverage', function() { describe('Coverage specs', function() {
it('should work', async function({page, server}) { describeChromeOnly('JSCoverage', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should work', async() => {
const { page, server } = getTestState();
await page.coverage.startJSCoverage(); await page.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/simple.html', {waitUntil: 'networkidle0'}); await page.goto(server.PREFIX + '/jscoverage/simple.html', {waitUntil: 'networkidle0'});
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
@ -31,27 +34,35 @@ module.exports.addTests = function({testRunner, expect}) {
{ start: 35, end: 61 }, { 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.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); await page.goto(server.PREFIX + '/jscoverage/sourceurl.html');
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
expect(coverage[0].url).toBe('nicename.js'); 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.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/eval.html'); await page.goto(server.PREFIX + '/jscoverage/eval.html');
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
expect(coverage.length).toBe(1); 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.coverage.startJSCoverage({reportAnonymousScripts: true});
await page.goto(server.PREFIX + '/jscoverage/eval.html'); await page.goto(server.PREFIX + '/jscoverage/eval.html');
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
expect(coverage.find(entry => entry.url.startsWith('debugger://'))).not.toBe(null); expect(coverage.find(entry => entry.url.startsWith('debugger://'))).not.toBe(null);
expect(coverage.length).toBe(2); 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.coverage.startJSCoverage({reportAnonymousScripts: true});
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.evaluate('console.log("foo")'); await page.evaluate('console.log("foo")');
@ -59,7 +70,9 @@ module.exports.addTests = function({testRunner, expect}) {
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
expect(coverage.length).toBe(0); 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.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.goto(server.PREFIX + '/jscoverage/multiple.html');
const coverage = await page.coverage.stopJSCoverage(); 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[0].url).toContain('/jscoverage/script1.js');
expect(coverage[1].url).toContain('/jscoverage/script2.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.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/ranges.html'); await page.goto(server.PREFIX + '/jscoverage/ranges.html');
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
@ -78,7 +93,9 @@ module.exports.addTests = function({testRunner, expect}) {
const range = entry.ranges[0]; const range = entry.ranges[0];
expect(entry.text.substring(range.start, range.end)).toBe(`console.log('used!');`); 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.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/unused.html'); await page.goto(server.PREFIX + '/jscoverage/unused.html');
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
@ -87,21 +104,28 @@ module.exports.addTests = function({testRunner, expect}) {
expect(entry.url).toContain('unused.html'); expect(entry.url).toContain('unused.html');
expect(entry.ranges.length).toBe(0); 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.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/involved.html'); await page.goto(server.PREFIX + '/jscoverage/involved.html');
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toBeGolden('jscoverage-involved.txt'); expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toBeGolden('jscoverage-involved.txt');
}); });
describe('resetOnNavigation', function() { 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.coverage.startJSCoverage({resetOnNavigation: false});
await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.goto(server.PREFIX + '/jscoverage/multiple.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const coverage = await page.coverage.stopJSCoverage(); const coverage = await page.coverage.stopJSCoverage();
expect(coverage.length).toBe(2); 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.coverage.startJSCoverage(); // Enabled by default.
await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.goto(server.PREFIX + '/jscoverage/multiple.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -110,7 +134,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
}); });
// @see https://crbug.com/990945 // @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.coverage.startJSCoverage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
@ -120,8 +146,13 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
}); });
describe('CSSCoverage', function() { describeChromeOnly('CSSCoverage', function() {
it('should work', async function({page, server}) { setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should work', async() => {
const { page, server } = getTestState();
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/simple.html'); await page.goto(server.PREFIX + '/csscoverage/simple.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
@ -133,14 +164,18 @@ module.exports.addTests = function({testRunner, expect}) {
const range = coverage[0].ranges[0]; const range = coverage[0].ranges[0];
expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }'); 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.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); await page.goto(server.PREFIX + '/csscoverage/sourceurl.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
expect(coverage[0].url).toBe('nicename.css'); 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.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.PREFIX + '/csscoverage/multiple.html');
const coverage = await page.coverage.stopCSSCoverage(); 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[0].url).toContain('/csscoverage/stylesheet1.css');
expect(coverage[1].url).toContain('/csscoverage/stylesheet2.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.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/unused.html'); await page.goto(server.PREFIX + '/csscoverage/unused.html');
const coverage = await page.coverage.stopCSSCoverage(); 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].url).toBe('unused.css');
expect(coverage[0].ranges.length).toBe(0); 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.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/media.html'); await page.goto(server.PREFIX + '/csscoverage/media.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
@ -167,13 +206,17 @@ module.exports.addTests = function({testRunner, expect}) {
{start: 17, end: 38} {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.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/involved.html'); await page.goto(server.PREFIX + '/csscoverage/involved.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).toBeGolden('csscoverage-involved.txt'); expect(JSON.stringify(coverage, null, 2).replace(/:\d{4}\//g, ':<PORT>/')).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.coverage.startCSSCoverage();
await page.addStyleTag({content: 'body { margin: 10px;}'}); await page.addStyleTag({content: 'body { margin: 10px;}'});
// trigger style recalc // trigger style recalc
@ -183,14 +226,18 @@ module.exports.addTests = function({testRunner, expect}) {
expect(coverage.length).toBe(0); expect(coverage.length).toBe(0);
}); });
describe('resetOnNavigation', function() { 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.coverage.startCSSCoverage({resetOnNavigation: false});
await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.PREFIX + '/csscoverage/multiple.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(2); 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.coverage.startCSSCoverage(); // Enabled by default.
await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.PREFIX + '/csscoverage/multiple.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -198,7 +245,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(coverage.length).toBe(0); 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.coverage.startCSSCoverage();
await page.evaluate(async url => { await page.evaluate(async url => {
document.body.textContent = 'hello, world'; document.body.textContent = 'hello, world';
@ -213,4 +262,4 @@ module.exports.addTests = function({testRunner, expect}) {
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
}); });
}); });
}; });

View File

@ -13,81 +13,76 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { describe('DefaultBrowserContext', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; itFailsFirefox('page.cookies() should work', async() => {
const { page, server } = getTestState();
describe('DefaultBrowserContext', function() { await page.goto(server.EMPTY_PAGE);
beforeEach(async state => { await page.evaluate(() => {
state.browser = await puppeteer.launch(defaultBrowserOptions); document.cookie = 'username=John Doe';
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
}]);
}); });
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
}]);
});
});

View File

@ -13,38 +13,42 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState,setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) { describe('Page.Events.Dialog', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; it('should fire', async() => {
const { page } = getTestState();
describe('Page.Events.Dialog', function() { page.on('dialog', dialog => {
it('should fire', async({page, server}) => { expect(dialog.type()).toBe('alert');
page.on('dialog', dialog => { expect(dialog.defaultValue()).toBe('');
expect(dialog.type()).toBe('alert'); expect(dialog.message()).toBe('yo');
expect(dialog.defaultValue()).toBe(''); dialog.accept();
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);
}); });
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);
});
});

View File

@ -14,38 +14,48 @@
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const utils = require('./utils'); const utils = require('./utils');
module.exports.addTests = function({testRunner, expect, CHROME}) { describe('ElementHandle specs', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
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.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const elementHandle = await page.$('.box:nth-of-type(13)'); const elementHandle = await page.$('.box:nth-of-type(13)');
const box = await elementHandle.boundingBox(); const box = await elementHandle.boundingBox();
expect(box).toEqual({ x: 100, y: 50, width: 50, height: 50 }); 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.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/frames/nested-frames.html'); await page.goto(server.PREFIX + '/frames/nested-frames.html');
const nestedFrame = page.frames()[1].childFrames()[1]; const nestedFrame = page.frames()[1].childFrames()[1];
const elementHandle = await nestedFrame.$('div'); const elementHandle = await nestedFrame.$('div');
const box = await elementHandle.boundingBox(); const box = await elementHandle.boundingBox();
if (CHROME) if (isChrome)
expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 }); expect(box).toEqual({ x: 28, y: 260, width: 264, height: 18 });
else else
expect(box).toEqual({ x: 28, y: 182, width: 254, height: 18 }); 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('<div style="display:none">hi</div>'); await page.setContent('<div style="display:none">hi</div>');
const element = await page.$('div'); const element = await page.$('div');
expect(await element.boundingBox()).toBe(null); 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.setViewport({ width: 500, height: 500 });
await page.setContent('<div style="width: 100px; height: 100px">hello</div>'); await page.setContent('<div style="width: 100px; height: 100px">hello</div>');
const elementHandle = await page.$('div'); const elementHandle = await page.$('div');
@ -53,7 +63,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const box = await elementHandle.boundingBox(); const box = await elementHandle.boundingBox();
expect(box).toEqual({ x: 8, y: 8, width: 100, height: 200 }); 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(` await page.setContent(`
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500"> <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
<rect id="theRect" x="30" y="50" width="200" height="300"></rect> <rect id="theRect" x="30" y="50" width="200" height="300"></rect>
@ -69,8 +81,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('ElementHandle.boxModel', function() { describeFailsFirefox('ElementHandle.boxModel', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/resetcss.html'); await page.goto(server.PREFIX + '/resetcss.html');
// Step 1: Add Frame and position it absolutely. // 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('<div style="display:none">hi</div>'); await page.setContent('<div style="display:none">hi</div>');
const element = await page.$('div'); const element = await page.$('div');
expect(await element.boxModel()).toBe(null); expect(await element.boxModel()).toBe(null);
@ -133,7 +149,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('ElementHandle.contentFrame', function() { 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 page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const elementHandle = await page.$('#frame1'); const elementHandle = await page.$('#frame1');
@ -143,26 +161,34 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('ElementHandle.click', function() { 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'); await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button'); const button = await page.$('button');
await button.click(); await button.click();
expect(await page.evaluate(() => result)).toBe('Clicked'); 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'); await page.goto(server.PREFIX + '/shadow.html');
const buttonHandle = await page.evaluateHandle(() => button); const buttonHandle = await page.evaluateHandle(() => button);
await buttonHandle.click(); await buttonHandle.click();
expect(await page.evaluate(() => clicked)).toBe(true); 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'); await page.goto(server.PREFIX + '/input/button.html');
const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild); const buttonTextNode = await page.evaluateHandle(() => document.querySelector('button').firstChild);
let error = null; let error = null;
await buttonTextNode.click().catch(err => error = err); await buttonTextNode.click().catch(err => error = err);
expect(error.message).toBe('Node is not of type HTMLElement'); 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'); await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button'); const button = await page.$('button');
await page.evaluate(button => button.remove(), 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); await button.click().catch(err => error = err);
expect(error.message).toBe('Node is detached from document'); 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'); await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button'); const button = await page.$('button');
await page.evaluate(button => button.style.display = 'none', button); await page.evaluate(button => button.style.display = 'none', button);
const error = await button.click().catch(err => err); const error = await button.click().catch(err => err);
expect(error.message).toBe('Node is either not visible or not an HTMLElement'); 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'); await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button'); const button = await page.$('button');
await page.evaluate(button => button.parentElement.style.display = 'none', button); await page.evaluate(button => button.parentElement.style.display = 'none', button);
const error = await button.click().catch(err => err); const error = await button.click().catch(err => err);
expect(error.message).toBe('Node is either not visible or not an HTMLElement'); expect(error.message).toBe('Node is either not visible or not an HTMLElement');
}); });
it_fails_ffox('should throw for <br> elements', async({page, server}) => { itFailsFirefox('should throw for <br> elements', async() => {
const { page } = getTestState();
await page.setContent('hello<br>goodbye'); await page.setContent('hello<br>goodbye');
const br = await page.$('br'); const br = await page.$('br');
const error = await br.click().catch(err => err); const error = await br.click().catch(err => err);
@ -193,7 +225,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('ElementHandle.hover', function() { 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'); await page.goto(server.PREFIX + '/input/scrollable.html');
const button = await page.$('#button-6'); const button = await page.$('#button-6');
await button.hover(); await button.hover();
@ -202,7 +236,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('ElementHandle.isIntersectingViewport', function() { 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'); await page.goto(server.PREFIX + '/offscreenbuttons.html');
for (let i = 0; i < 11; ++i) { for (let i = 0; i < 11; ++i) {
const button = await page.$('#btn' + i); const button = await page.$('#btn' + i);
@ -212,4 +248,4 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
} }
}); });
}); });
}; });

View File

@ -14,21 +14,33 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, puppeteer}) { const expect = require('expect');
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const iPhone = puppeteer.devices['iPhone 6']; describe('Emulation', () => {
const iPhoneLandscape = puppeteer.devices['iPhone 6 landscape']; 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() { 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}); expect(page.viewport()).toEqual({width: 800, height: 600});
await page.setViewport({width: 123, height: 456}); await page.setViewport({width: 123, height: 456});
expect(page.viewport()).toEqual({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'); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(800); expect(await page.evaluate(() => window.innerWidth)).toBe(800);
await page.setViewport(iPhone.viewport); await page.setViewport(iPhone.viewport);
@ -36,7 +48,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
await page.setViewport({width: 400, height: 300}); await page.setViewport({width: 400, height: 300});
expect(await page.evaluate(() => window.innerWidth)).toBe(400); 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'); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false); expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
await page.setViewport(iPhone.viewport); await page.setViewport(iPhone.viewport);
@ -58,19 +72,25 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
return promise; 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'); await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO'); expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
await page.setViewport(iPhone.viewport); await page.setViewport(iPhone.viewport);
await page.goto(server.PREFIX + '/detect-touch.html'); await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); 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.setViewport({ width: 800, height: 600, hasTouch: true });
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true); 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'); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary'); expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary');
await page.setViewport(iPhoneLandscape.viewport); await page.setViewport(iPhoneLandscape.viewport);
@ -81,13 +101,17 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
describe('Page.emulate', function() { 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.goto(server.PREFIX + '/mobile.html');
await page.emulate(iPhone); await page.emulate(iPhone);
expect(await page.evaluate(() => window.innerWidth)).toBe(375); expect(await page.evaluate(() => window.innerWidth)).toBe(375);
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone'); 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.emulate(iPhone);
await page.goto(server.PREFIX + '/input/button.html'); await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button'); const button = await page.$('button');
@ -98,7 +122,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
describe('Page.emulateMedia [deprecated]', function() { 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 * don't want to remove it from Puppeteer just yet. We can't check
* that emulateMedia === emulateMediaType because when running tests * that emulateMedia === emulateMediaType because when running tests
* with COVERAGE=1 the methods get rewritten. So instead we * 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 * If you update these tests, you should update emulateMediaType's
* tests, and vice-versa. * 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('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
await page.emulateMedia('print'); 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('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); 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; let error = null;
await page.emulateMedia('bad').catch(e => error = e); await page.emulateMedia('bad').catch(e => error = e);
expect(error.message).toBe('Unsupported media type: bad'); expect(error.message).toBe('Unsupported media type: bad');
@ -126,10 +154,12 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
describe('Page.emulateMediaType', function() { 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). * 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('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
await page.emulateMediaType('print'); 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('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false); 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; let error = null;
await page.emulateMediaType('bad').catch(e => error = e); await page.emulateMediaType('bad').catch(e => error = e);
expect(error.message).toBe('Unsupported media type: bad'); expect(error.message).toBe('Unsupported media type: bad');
@ -147,7 +179,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
describe('Page.emulateMediaFeatures', function() { describe('Page.emulateMediaFeatures', function() {
it_fails_ffox('should work', async({page, server}) => { itFailsFirefox('should work', async() => {
const { page } = getTestState();
await page.emulateMediaFeatures([ await page.emulateMediaFeatures([
{ name: 'prefers-reduced-motion', value: 'reduce' }, { 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: dark)').matches)).toBe(false);
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').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; let error = null;
await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e); await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e);
expect(error.message).toBe('Unsupported media feature: bad'); expect(error.message).toBe('Unsupported media feature: bad');
}); });
}); });
describe_fails_ffox('Page.emulateTimezone', function() { describeFailsFirefox('Page.emulateTimezone', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
page.evaluate(() => { page.evaluate(() => {
globalThis.date = new Date(1479579154987); 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)'); 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; let error = null;
await page.emulateTimezone('Foo/Bar').catch(e => error = e); await page.emulateTimezone('Foo/Bar').catch(e => error = e);
expect(error.message).toBe('Invalid timezone ID: Foo/Bar'); expect(error.message).toBe('Invalid timezone ID: Foo/Bar');
@ -209,4 +249,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
}); });
}; });

View File

@ -15,59 +15,85 @@
*/ */
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const bigint = typeof BigInt !== 'undefined'; const bigint = typeof BigInt !== 'undefined';
module.exports.addTests = function({testRunner, expect}) { describe('Evaluation specs', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.evaluate', function() { describe('Page.evaluate', function() {
it('should work', async({page, server}) => {
it('should work', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => 7 * 3); const result = await page.evaluate(() => 7 * 3);
expect(result).toBe(21); 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)); const result = await page.evaluate(a => a, BigInt(42));
expect(result).toBe(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); const result = await page.evaluate(a => a, NaN);
expect(Object.is(result, NaN)).toBe(true); 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); const result = await page.evaluate(a => a, -0);
expect(Object.is(result, -0)).toBe(true); 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); const result = await page.evaluate(a => a, Infinity);
expect(Object.is(result, Infinity)).toBe(true); 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); const result = await page.evaluate(a => a, -Infinity);
expect(Object.is(result, -Infinity)).toBe(true); 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]); const result = await page.evaluate(a => a, [1, 2, 3]);
expect(result).toEqual([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]); const result = await page.evaluate(a => Array.isArray(a), [1, 2, 3]);
expect(result).toBe(true); 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); await page.evaluate(() => window.globalVar = 123);
expect(await page.evaluate('globalVar')).toBe(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'); await page.goto(server.PREFIX + '/global-var.html');
expect(await page.evaluate('globalVar')).toBe(123); 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); 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 = { const a = {
sum(a, b) { return a + b; }, 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.sum, 1, 2)).toBe(3);
expect(await page.evaluate(a.mult, 2, 4)).toBe(8); 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}); const result = await page.evaluate(a => a['中文字符'], {'中文字符': 42});
expect(result).toBe(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; let error = null;
await page.evaluate(() => { await page.evaluate(() => {
location.reload(); location.reload();
@ -88,11 +118,15 @@ module.exports.addTests = function({testRunner, expect}) {
}).catch(e => error = e); }).catch(e => error = e);
expect(error.message).toContain('Protocol error'); 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)); const result = await page.evaluate(() => Promise.resolve(8 * 7));
expect(result).toBe(56); 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; let frameEvaluation = null;
page.on('framenavigated', async frame => { page.on('framenavigated', async frame => {
frameEvaluation = frame.evaluate(() => 6 * 7); frameEvaluation = frame.evaluate(() => 6 * 7);
@ -100,7 +134,9 @@ module.exports.addTests = function({testRunner, expect}) {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(await frameEvaluation).toBe(42); 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 // Setup inpage callback, which calls Page.evaluate
await page.exposeFunction('callController', async function(a, b) { await page.exposeFunction('callController', async function(a, b) {
return await page.evaluate((a, b) => a * b, 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); 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; let error = null;
await page.evaluate(() => not_existing_object.property).catch(e => error = e); await page.evaluate(() => not_existing_object.property).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('not_existing_object'); 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; let error = null;
await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e); await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('qwerty'); 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; let error = null;
await page.evaluate(() => { throw 100500; }).catch(e => error = e); await page.evaluate(() => { throw 100500; }).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('100500'); 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 object = {foo: 'bar!'};
const result = await page.evaluate(a => a, object); const result = await page.evaluate(a => a, object);
expect(result).not.toBe(object); expect(result).not.toBe(object);
expect(result).toEqual(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)); const result = await page.evaluate(() => BigInt(42));
expect(result).toBe(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); const result = await page.evaluate(() => NaN);
expect(Object.is(result, NaN)).toBe(true); 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); const result = await page.evaluate(() => -0);
expect(Object.is(result, -0)).toBe(true); 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); const result = await page.evaluate(() => Infinity);
expect(Object.is(result, Infinity)).toBe(true); 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); const result = await page.evaluate(() => -Infinity);
expect(Object.is(result, -Infinity)).toBe(true); 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'); const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo');
expect(result).toBe(true); 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({}); 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); 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 result = await page.evaluate(() => {
const a = {}; const a = {};
const b = {a}; const b = {a};
@ -173,7 +235,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
expect(result).toBe(undefined); 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 windowHandle = await page.evaluateHandle(() => window);
const errorText = await windowHandle.jsonValue().catch(e => e.message); const errorText = await windowHandle.jsonValue().catch(e => e.message);
const error = await page.evaluate(errorText => { const error = await page.evaluate(errorText => {
@ -181,25 +245,35 @@ module.exports.addTests = function({testRunner, expect}) {
}, errorText).catch(e => e); }, errorText).catch(e => e);
expect(error.message).toContain(errorText); 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'); const result = await page.evaluate('1 + 2');
expect(result).toBe(3); 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;'); const result = await page.evaluate('1 + 5;');
expect(result).toBe(6); 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!'); const result = await page.evaluate('2 + 5;\n// do some math!');
expect(result).toBe(7); 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('<section>42</section>'); await page.setContent('<section>42</section>');
const element = await page.$('section'); const element = await page.$('section');
const text = await page.evaluate(e => e.textContent, element); const text = await page.evaluate(e => e.textContent, element);
expect(text).toBe('42'); 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('<section>39</section>'); await page.setContent('<section>39</section>');
const element = await page.$('section'); const element = await page.$('section');
expect(element).toBeTruthy(); expect(element).toBeTruthy();
@ -208,7 +282,9 @@ module.exports.addTests = function({testRunner, expect}) {
await page.evaluate(e => e.textContent, element).catch(e => error = e); await page.evaluate(e => e.textContent, element).catch(e => error = e);
expect(error.message).toContain('JSHandle is disposed'); 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); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const bodyHandle = await page.frames()[1].$('body'); const bodyHandle = await page.frames()[1].$('body');
let error = null; let error = null;
@ -216,7 +292,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('JSHandles can be evaluated only in the context they were created'); 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(() => { const result = await page.evaluate(() => {
document.body.appendChild(document.createTextNode('test')); document.body.appendChild(document.createTextNode('test'));
document.execCommand('selectAll'); document.execCommand('selectAll');
@ -224,7 +302,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
expect(result).toBe(true); 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(); const executionContext = await page.mainFrame().executionContext();
await Promise.all([ await Promise.all([
@ -234,7 +314,9 @@ module.exports.addTests = function({testRunner, expect}) {
const error = await executionContext.evaluate(() => null).catch(e => e); const error = await executionContext.evaluate(() => null).catch(e => e);
expect(error.message).toContain('navigation'); 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'); await page.goto(server.PREFIX + '/one-style.html');
const result = await page.evaluate(() => { const result = await page.evaluate(() => {
window.location = '/empty.html'; window.location = '/empty.html';
@ -242,11 +324,15 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
expect(result).toEqual([42]); 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')); const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a'));
expect(a.length).toBe(100 * 1024 * 1024); 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; let error = null;
await page.evaluate(() => new Promise(() => { await page.evaluate(() => new Promise(() => {
throw new Error('Error in promise'); throw new Error('Error in promise');
@ -255,15 +341,19 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
}); });
describe_fails_ffox('Page.evaluateOnNewDocument', function() { describeFailsFirefox('Page.evaluateOnNewDocument', function() {
it('should evaluate before anything else on the page', async({page, server}) => { it('should evaluate before anything else on the page', async() => {
const { page, server } = getTestState();
await page.evaluateOnNewDocument(function(){ await page.evaluateOnNewDocument(function(){
window.injected = 123; window.injected = 123;
}); });
await page.goto(server.PREFIX + '/tamperable.html'); await page.goto(server.PREFIX + '/tamperable.html');
expect(await page.evaluate(() => window.result)).toBe(123); 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); server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
await page.evaluateOnNewDocument(function(){ await page.evaluateOnNewDocument(function(){
window.injected = 123; window.injected = 123;
@ -278,7 +368,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
describe('Frame.evaluate', function() { 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 page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(page.frames().length).toBe(2); 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()[0].evaluate(() => window.FOO)).toBe('foo');
expect(await page.frames()[1].evaluate(() => window.FOO)).toBe('bar'); 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'); await page.goto(server.PREFIX + '/frames/one-frame.html');
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
expect(await page.frames()[0].evaluate(() => document.body.textContent.trim())).toBe(''); 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`); 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); await page.goto(server.EMPTY_PAGE);
const mainFrame = page.mainFrame(); const mainFrame = page.mainFrame();
expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost'); 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'); expect(await mainFrame.evaluate(() => window.location.href)).toContain('127');
}); });
}); });
}; });

View File

@ -14,59 +14,62 @@
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
const path = require('path'); const path = require('path');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, puppeteerPath}) { describe('Fixtures', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; itFailsFirefox('dumpio option should work with pipe option ', async() => {
const {it, fit, xit, it_fails_ffox} = testRunner; const { defaultBrowserOptions, puppeteerPath } = getTestState();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Fixtures', function() { let dumpioData = '';
it_fails_ffox('dumpio option should work with pipe option ', async({server}) => { const {spawn} = require('child_process');
let dumpioData = ''; const options = Object.assign({}, defaultBrowserOptions, {pipe: true, dumpio: true});
const {spawn} = require('child_process'); const res = spawn('node',
const options = Object.assign({}, defaultBrowserOptions, {pipe: true, dumpio: true}); [path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]);
const res = spawn('node', res.stderr.on('data', data => dumpioData += data.toString('utf8'));
[path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]); await new Promise(resolve => res.on('close', resolve));
res.stderr.on('data', data => dumpioData += data.toString('utf8')); expect(dumpioData).toContain('message from dumpio');
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);
});
}); });
}; 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);
});
});

View File

@ -15,14 +15,17 @@
*/ */
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) { describe('Frame specs', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Frame.executionContext', function() { 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 page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
@ -49,7 +52,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
describe('Frame.evaluateHandle', function() { describe('Frame.evaluateHandle', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const mainFrame = page.mainFrame(); const mainFrame = page.mainFrame();
const windowHandle = await mainFrame.evaluateHandle(() => window); const windowHandle = await mainFrame.evaluateHandle(() => window);
@ -58,7 +63,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
describe('Frame.evaluate', function() { 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); const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.detachFrame(page, 'frame1'); await utils.detachFrame(page, 'frame1');
let error = null; let error = null;
@ -68,7 +75,9 @@ module.exports.addTests = function({testRunner, expect}) {
}); });
describe('Frame Management', function() { 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'); await page.goto(server.PREFIX + '/frames/nested-frames.html');
expect(utils.dumpFrames(page.mainFrame())).toEqual([ expect(utils.dumpFrames(page.mainFrame())).toEqual([
'http://localhost:<PORT>/frames/nested-frames.html', 'http://localhost:<PORT>/frames/nested-frames.html',
@ -78,7 +87,9 @@ module.exports.addTests = function({testRunner, expect}) {
' http://localhost:<PORT>/frames/frame.html (aframe)' ' http://localhost:<PORT>/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); await page.goto(server.EMPTY_PAGE);
// validate frameattached events // validate frameattached events
const attachedFrames = []; const attachedFrames = [];
@ -101,7 +112,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(detachedFrames.length).toBe(1); expect(detachedFrames.length).toBe(1);
expect(detachedFrames[0].isDetached()).toBe(true); 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 page.goto(server.EMPTY_PAGE);
await Promise.all([ await Promise.all([
page.goto(server.EMPTY_PAGE + '#foo'), page.goto(server.EMPTY_PAGE + '#foo'),
@ -109,20 +122,26 @@ module.exports.addTests = function({testRunner, expect}) {
]); ]);
expect(page.url()).toBe(server.EMPTY_PAGE + '#foo'); 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); await page.goto(server.EMPTY_PAGE);
const mainFrame = page.mainFrame(); const mainFrame = page.mainFrame();
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(page.mainFrame() === mainFrame).toBeTruthy(); 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; let hasEvents = false;
page.on('frameattached', frame => hasEvents = true); page.on('frameattached', frame => hasEvents = true);
page.on('framedetached', frame => hasEvents = true); page.on('framedetached', frame => hasEvents = true);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(hasEvents).toBe(false); 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 attachedFrames = [];
let detachedFrames = []; let detachedFrames = [];
let navigatedFrames = []; let navigatedFrames = [];
@ -142,7 +161,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(detachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(4);
expect(navigatedFrames.length).toBe(1); 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 attachedFrames = [];
let detachedFrames = []; let detachedFrames = [];
let navigatedFrames = []; let navigatedFrames = [];
@ -162,7 +183,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(detachedFrames.length).toBe(4); expect(detachedFrames.length).toBe(4);
expect(navigatedFrames.length).toBe(1); 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.goto(server.PREFIX + '/shadow.html');
await page.evaluate(async url => { await page.evaluate(async url => {
const frame = document.createElement('iframe'); const frame = document.createElement('iframe');
@ -173,7 +196,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
expect(page.frames()[1].url()).toBe(server.EMPTY_PAGE); 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 utils.attachFrame(page, 'theFrameId', server.EMPTY_PAGE);
await page.evaluate(url => { await page.evaluate(url => {
const frame = document.createElement('iframe'); 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()[1].name()).toBe('theFrameId');
expect(page.frames()[2].name()).toBe('theFrameName'); 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, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
expect(page.frames()[0].parentFrame()).toBe(null); expect(page.frames()[0].parentFrame()).toBe(null);
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame()); expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
expect(page.frames()[2].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); const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
window.frame = document.querySelector('#frame1'); window.frame = document.querySelector('#frame1');
@ -208,4 +237,4 @@ module.exports.addTests = function({testRunner, expect}) {
expect(frame1).not.toBe(frame2); expect(frame1).not.toBe(frame2);
}); });
}); });
}; });

View File

@ -17,37 +17,51 @@
const path = require('path'); const path = require('path');
const os = require('os'); const os = require('os');
const fs = require('fs'); const fs = require('fs');
const util = require('util'); const {promisify} = require('util');
const utils = require('./utils'); const {waitEvent} = require('./utils');
const {waitEvent} = utils; const expect = require('expect');
const {getTestState} = require('./mocha-utils');
const rmAsync = util.promisify(require('rimraf')); const rmAsync = promisify(require('rimraf'));
const mkdtempAsync = util.promisify(fs.mkdtemp); const mkdtempAsync = promisify(fs.mkdtemp);
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowserOptions}) { const extensionPath = path.join(__dirname, 'assets', 'simple-extension');
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner; describeChromeOnly('headful tests', function() {
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
/* 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() { describe('HEADFUL', function() {
it('background_page target type should be available', async() => { it('background_page target type should be available', async() => {
const {puppeteer} = getTestState();
const browserWithExtension = await puppeteer.launch(extensionOptions); const browserWithExtension = await puppeteer.launch(extensionOptions);
const page = await browserWithExtension.newPage(); const page = await browserWithExtension.newPage();
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page'); 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(); await browserWithExtension.close();
expect(backgroundPageTarget).toBeTruthy(); 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 browserWithExtension = await puppeteer.launch(extensionOptions);
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page'); const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page');
const page = await backgroundPageTarget.page(); const page = await backgroundPageTarget.page();
@ -64,12 +79,15 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await browserWithExtension.close(); await browserWithExtension.close();
}); });
it('should have default url when launching browser', async function() { it('should have default url when launching browser', async function() {
const {puppeteer} = getTestState();
const browser = await puppeteer.launch(extensionOptions); const browser = await puppeteer.launch(extensionOptions);
const pages = (await browser.pages()).map(page => page.url()); const pages = (await browser.pages()).map(page => page.url());
expect(pages).toEqual(['about:blank']); expect(pages).toEqual(['about:blank']);
await browser.close(); 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); const userDataDir = await mkdtempAsync(TMP_FOLDER);
// Write a cookie in headful chrome // Write a cookie in headful chrome
const headfulBrowser = await puppeteer.launch(Object.assign({userDataDir}, headfulOptions)); 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'); expect(cookie).toBe('foo=true');
}); });
// TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548 // 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. // https://google.com is isolated by default in Chromium embedder.
const browser = await puppeteer.launch(headfulOptions); const browser = await puppeteer.launch(headfulOptions);
const page = await browser.newPage(); const page = await browser.newPage();
@ -109,7 +129,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
]); ]);
await browser.close(); 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 browser = await puppeteer.launch(headfulOptions);
const page = await browser.newPage(); const page = await browser.newPage();
await page.goto(server.PREFIX + '/beforeunload.html'); await page.goto(server.PREFIX + '/beforeunload.html');
@ -118,7 +140,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await page.click('body'); await page.click('body');
await browser.close(); 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 browser = await puppeteer.launch(Object.assign({devtools: true}, headfulOptions));
const context = await browser.createIncognitoBrowserContext(); const context = await browser.createIncognitoBrowserContext();
await Promise.all([ await Promise.all([
@ -131,6 +155,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
describe('Page.bringToFront', function() { describe('Page.bringToFront', function() {
it('should work', async() => { it('should work', async() => {
const {puppeteer} = getTestState();
const browser = await puppeteer.launch(headfulOptions); const browser = await puppeteer.launch(headfulOptions);
const page1 = await browser.newPage(); const page1 = await browser.newPage();
const page2 = await browser.newPage(); const page2 = await browser.newPage();
@ -148,5 +173,6 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await browser.close(); await browser.close();
}); });
}); });
};
});

View File

@ -14,85 +14,107 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { const expect = require('expect');
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; const {getTestState} = require('./mocha-utils');
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;
});
describe('Response.securityDetails', function() { describeFailsFirefox('ignoreHTTPSErrors', function() {
it('should work', async({page, httpsServer}) => { /* Note that this test creates its own browser rather than use
const [serverRequest, response] = await Promise.all([ * the one provided by the test set-up as we need one
httpsServer.waitForRequest('/empty.html'), * with ignoreHTTPSErrors set to true
page.goto(httpsServer.EMPTY_PAGE) */
]); let browser;
const securityDetails = response.securityDetails(); let context;
expect(securityDetails.issuer()).toBe('puppeteer-tests'); let page;
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);
});
});
it('should work', async({page, httpsServer}) => { before(async() => {
let error = null; const {defaultBrowserOptions, puppeteer} = getTestState();
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions);
expect(error).toBe(null); browser = await puppeteer.launch(options);
expect(response.ok()).toBe(true); });
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}) => { it('should be |null| for non-secure requests', async() => {
await page.setRequestInterception(true); const { server } = getTestState();
page.on('request', request => request.continue());
const response = await page.goto(httpsServer.EMPTY_PAGE); const response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(200); expect(response.securityDetails()).toBe(null);
}); });
it('should work with mixed content', async({page, server, httpsServer}) => { it('Network redirects should report SecurityDetails', async() => {
httpsServer.setRoute('/mixedcontent.html', (req, res) => { const { httpsServer } = getTestState();
res.end(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
}); httpsServer.setRedirect('/plzredirect', '/empty.html');
await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'load'}); const responses = [];
expect(page.frames().length).toBe(2); page.on('response', response => responses.push(response));
// Make sure blocked iframe has functional execution context const [serverRequest, ] = await Promise.all([
// @see https://github.com/puppeteer/puppeteer/issues/2709 httpsServer.waitForRequest('/plzredirect'),
expect(await page.frames()[0].evaluate('1 + 2')).toBe(3); page.goto(httpsServer.PREFIX + '/plzredirect')
expect(await page.frames()[1].evaluate('2 + 3')).toBe(5); ]);
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(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
});
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);
});
});

View File

@ -15,15 +15,19 @@
*/ */
const path = require('path'); 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'); const FILE_TO_UPLOAD = path.join(__dirname, '/assets/file-to-upload.txt');
module.exports.addTests = function({testRunner, expect, puppeteer}) { describe('input tests', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe_fails_ffox('input', function() { describeFailsFirefox('input', function() {
it('should upload the file', async({page, server}) => { it('should upload the file', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/fileupload.html'); await page.goto(server.PREFIX + '/input/fileupload.html');
const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD); const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD);
const input = await page.$('input'); const input = await page.$('input');
@ -44,8 +48,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
}); });
describe_fails_ffox('Page.waitForFileChooser', function() { describeFailsFirefox('Page.waitForFileChooser', function() {
it('should work when file input is attached to DOM', async({page, server}) => { it('should work when file input is attached to DOM', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -53,7 +59,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]); ]);
expect(chooser).toBeTruthy(); 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([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
page.evaluate(() => { page.evaluate(() => {
@ -64,24 +72,32 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]); ]);
expect(chooser).toBeTruthy(); expect(chooser).toBeTruthy();
}); });
it('should respect timeout', async({page, server}) => { it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null; let error = null;
await page.waitForFileChooser({timeout: 1}).catch(e => error = e); await page.waitForFileChooser({timeout: 1}).catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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); page.setDefaultTimeout(1);
let error = null; let error = null;
await page.waitForFileChooser().catch(e => error = e); await page.waitForFileChooser().catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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); page.setDefaultTimeout(0);
let error = null; let error = null;
await page.waitForFileChooser({timeout: 1}).catch(e => error = e); await page.waitForFileChooser({timeout: 1}).catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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([ const [chooser] = await Promise.all([
page.waitForFileChooser({timeout: 0}), page.waitForFileChooser({timeout: 0}),
page.evaluate(() => setTimeout(() => { page.evaluate(() => setTimeout(() => {
@ -92,7 +108,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]); ]);
expect(chooser).toBeTruthy(); 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(`<input type=file>`); await page.setContent(`<input type=file>`);
const [fileChooser1, fileChooser2] = await Promise.all([ const [fileChooser1, fileChooser2] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -103,8 +121,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
}); });
describe_fails_ffox('FileChooser.accept', function() { describeFailsFirefox('FileChooser.accept', function() {
it('should accept single file', async({page, server}) => { it('should accept single file', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`); await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), 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.length)).toBe(1);
expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt'); 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(`<input type=file>`); await page.setContent(`<input type=file>`);
page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD])); page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD]));
expect(await page.$eval('input', async picker => { expect(await page.$eval('input', async picker => {
@ -129,7 +151,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
return promise.then(() => reader.result); return promise.then(() => reader.result);
})).toBe('contents of the file'); })).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(`<input type=file>`); await page.setContent(`<input type=file>`);
page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD])); page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD]));
expect(await page.$eval('input', async picker => { expect(await page.$eval('input', async picker => {
@ -144,7 +168,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
return picker.files.length; return picker.files.length;
})).toBe(0); })).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(`<input type=file>`); await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -157,7 +183,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]).catch(e => error = e); ]).catch(e => error = e);
expect(error).not.toBe(null); 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(`<input type=file>`); await page.setContent(`<input type=file>`);
const [fileChooser] = await Promise.all([ const [fileChooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -170,8 +198,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
}); });
describe_fails_ffox('FileChooser.cancel', function() { describeFailsFirefox('FileChooser.cancel', function() {
it('should cancel dialog', async({page, server}) => { it('should cancel dialog', async() => {
const { page } = getTestState();
// Consider file chooser canceled if we can summon another one. // Consider file chooser canceled if we can summon another one.
// There's no reliable way in WebPlatform to see that FileChooser was // There's no reliable way in WebPlatform to see that FileChooser was
// canceled. // canceled.
@ -187,7 +217,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
page.$eval('input', input => input.click()), 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(`<input type=file>`); await page.setContent(`<input type=file>`);
const [fileChooser] = await Promise.all([ const [fileChooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -200,8 +232,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
}); });
}); });
describe_fails_ffox('FileChooser.isMultiple', () => { describeFailsFirefox('FileChooser.isMultiple', () => {
it('should work for single file pick', async({page, server}) => { it('should work for single file pick', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`); await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -209,7 +243,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]); ]);
expect(chooser.isMultiple()).toBe(false); 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(`<input multiple type=file>`); await page.setContent(`<input multiple type=file>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -217,7 +253,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]); ]);
expect(chooser.isMultiple()).toBe(true); 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(`<input multiple webkitdirectory type=file>`); await page.setContent(`<input multiple webkitdirectory type=file>`);
const [chooser] = await Promise.all([ const [chooser] = await Promise.all([
page.waitForFileChooser(), page.waitForFileChooser(),
@ -226,4 +264,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(chooser.isMultiple()).toBe(true); expect(chooser.isMultiple()).toBe(true);
}); });
}); });
}; });

View File

@ -14,27 +14,37 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, CHROME}) { const expect = require('expect');
const {describe, xdescribe, fdescribe} = testRunner; const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describe('JSHandle', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Page.evaluateHandle', function() { describe('Page.evaluateHandle', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
const windowHandle = await page.evaluateHandle(() => window); const windowHandle = await page.evaluateHandle(() => window);
expect(windowHandle).toBeTruthy(); 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 navigatorHandle = await page.evaluateHandle(() => navigator);
const text = await page.evaluate(e => e.userAgent, navigatorHandle); const text = await page.evaluate(e => e.userAgent, navigatorHandle);
expect(text).toContain('Mozilla'); 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 aHandle = await page.evaluateHandle(() => 5);
const isFive = await page.evaluate(e => Object.is(e, 5), aHandle); const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
expect(isFive).toBeTruthy(); 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); const aHandle = await page.evaluateHandle(() => document.body);
let error = null; let error = null;
await page.evaluateHandle( await page.evaluateHandle(
@ -43,18 +53,24 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
).catch(e => error = e); ).catch(e => error = e);
expect(error.message).toContain('Are you passing a nested JSHandle?'); 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); const aHandle = await page.evaluateHandle(() => Infinity);
expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true); 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(() => { const aHandle = await page.evaluateHandle(() => {
window.FOO = 123; window.FOO = 123;
return window; return window;
}); });
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123); 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(() => { const aHandle = await page.evaluateHandle(() => {
window.FOO = 123; window.FOO = 123;
return window; return window;
@ -64,7 +80,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('JSHandle.getProperty', function() { describe('JSHandle.getProperty', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
const aHandle = await page.evaluateHandle(() => ({ const aHandle = await page.evaluateHandle(() => ({
one: 1, one: 1,
two: 2, two: 2,
@ -76,21 +94,27 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('JSHandle.jsonValue', function() { 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 aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
const json = await aHandle.jsonValue(); const json = await aHandle.jsonValue();
expect(json).toEqual({foo: 'bar'}); 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 dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
const json = await dateHandle.jsonValue(); const json = await dateHandle.jsonValue();
expect(json).toEqual({}); 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'); const windowHandle = await page.evaluateHandle('window');
let error = null; let error = null;
await windowHandle.jsonValue().catch(e => error = e); await windowHandle.jsonValue().catch(e => error = e);
if (CHROME) if (isChrome)
expect(error.message).toContain('Object reference chain is too long'); expect(error.message).toContain('Object reference chain is too long');
else else
expect(error.message).toContain('Object is not serializable'); expect(error.message).toContain('Object is not serializable');
@ -98,7 +122,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('JSHandle.getProperties', function() { describe('JSHandle.getProperties', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
const aHandle = await page.evaluateHandle(() => ({ const aHandle = await page.evaluateHandle(() => ({
foo: 'bar' foo: 'bar'
})); }));
@ -107,7 +133,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(foo).toBeTruthy(); expect(foo).toBeTruthy();
expect(await foo.jsonValue()).toBe('bar'); 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(() => { const aHandle = await page.evaluateHandle(() => {
class A { class A {
constructor() { constructor() {
@ -129,24 +157,32 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('JSHandle.asElement', function() { 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 aHandle = await page.evaluateHandle(() => document.body);
const element = aHandle.asElement(); const element = aHandle.asElement();
expect(element).toBeTruthy(); 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 aHandle = await page.evaluateHandle(() => 2);
const element = aHandle.asElement(); const element = aHandle.asElement();
expect(element).toBeFalsy(); 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('<div>ee!</div>'); await page.setContent('<div>ee!</div>');
const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild); const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
const element = aHandle.asElement(); const element = aHandle.asElement();
expect(element).toBeTruthy(); expect(element).toBeTruthy();
expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element)); 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('<section>test</section>'); await page.setContent('<section>test</section>');
await page.evaluate(() => delete Node); await page.evaluate(() => delete Node);
const handle = await page.evaluateHandle(() => document.querySelector('section')); const handle = await page.evaluateHandle(() => document.querySelector('section'));
@ -156,17 +192,23 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('JSHandle.toString', function() { 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); const numberHandle = await page.evaluateHandle(() => 2);
expect(numberHandle.toString()).toBe('JSHandle:2'); expect(numberHandle.toString()).toBe('JSHandle:2');
const stringHandle = await page.evaluateHandle(() => 'a'); const stringHandle = await page.evaluateHandle(() => 'a');
expect(stringHandle.toString()).toBe('JSHandle: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); const aHandle = await page.evaluateHandle(() => window);
expect(aHandle.toString()).toBe('JSHandle@object'); 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('(function(){})')).toString()).toBe('JSHandle@function');
expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle:12'); expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle:12');
expect((await page.evaluateHandle('true')).toString()).toBe('JSHandle:true'); 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'); expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy');
}); });
}); });
}; });

View File

@ -16,239 +16,274 @@
const utils = require('./utils'); const utils = require('./utils');
const os = require('os'); const os = require('os');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, FFOX}) { describe('Keyboard', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Keyboard', function() { it('should type into a textarea', async() => {
it('should type into a textarea', async({page, server}) => { 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);
});
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);
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);
});
});

View File

@ -23,15 +23,15 @@ const readFileAsync = helper.promisify(fs.readFile);
const statAsync = helper.promisify(fs.stat); const statAsync = helper.promisify(fs.stat);
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-'); const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, CHROME, FFOX, puppeteerPath}) { describe('Launcher specs', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Puppeteer', function() { describe('Puppeteer', function() {
describe('BrowserFetcher', 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 downloadsFolder = await mkdtempAsync(TMP_FOLDER);
const browserFetcher = puppeteer.createBrowserFetcher({ const browserFetcher = puppeteer.createBrowserFetcher({
platform: 'linux', platform: 'linux',
@ -61,9 +61,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
expect(await browserFetcher.localRevisions()).toEqual([]); expect(await browserFetcher.localRevisions()).toEqual([]);
await rmAsync(downloadsFolder); await rmAsync(downloadsFolder);
}); });
}); it('should download and extract firefox linux binary', async() => {
describe('BrowserFetcher', function() { const { server , puppeteer} = getTestState();
it('should download and extract firefox linux binary', async({server}) => {
const downloadsFolder = await mkdtempAsync(TMP_FOLDER); const downloadsFolder = await mkdtempAsync(TMP_FOLDER);
const browserFetcher = puppeteer.createBrowserFetcher({ const browserFetcher = puppeteer.createBrowserFetcher({
platform: 'linux', platform: 'linux',
@ -94,8 +94,10 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await rmAsync(downloadsFolder); await rmAsync(downloadsFolder);
}); });
}); });
describe('Browser.disconnect', function() { 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', () => {}); server.setRoute('/one-style.css', () => {});
const browser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.launch(defaultBrowserOptions);
const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()}); 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!'); expect(error.message).toBe('Navigation failed because browser has disconnected!');
await browser.close(); 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', () => {}); server.setRoute('/empty.html', () => {});
const browser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.launch(defaultBrowserOptions);
const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()}); const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()});
@ -120,7 +124,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
}); });
}); });
describe('Browser.close', function() { 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 browser = await puppeteer.launch(defaultBrowserOptions);
const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()}); const remote = await puppeteer.connect({browserWSEndpoint: browser.wsEndpoint()});
const newPage = await remote.newPage(); const newPage = await remote.newPage();
@ -134,10 +140,12 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
expect(message).toContain('Target closed'); expect(message).toContain('Target closed');
expect(message).not.toContain('Timeout'); expect(message).not.toContain('Timeout');
} }
await browser.close();
}); });
}); });
describe('Puppeteer.launch', function() { describe('Puppeteer.launch', function() {
it('should reject all promises when browser is closed', async() => { it('should reject all promises when browser is closed', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const browser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.launch(defaultBrowserOptions);
const page = await browser.newPage(); const page = await browser.newPage();
let error = null; let error = null;
@ -146,13 +154,17 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await neverResolves; await neverResolves;
expect(error.message).toContain('Protocol error'); 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; let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'}); const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'});
await puppeteer.launch(options).catch(e => waitError = e); await puppeteer.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('Failed to launch'); 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 userDataDir = await mkdtempAsync(TMP_FOLDER);
const options = Object.assign({userDataDir}, defaultBrowserOptions); const options = Object.assign({userDataDir}, defaultBrowserOptions);
const browser = await puppeteer.launch(options); 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 // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(e => {}); 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 userDataDir = await mkdtempAsync(TMP_FOLDER);
const options = Object.assign({}, defaultBrowserOptions); const options = Object.assign({}, defaultBrowserOptions);
if (CHROME) { if (isChrome) {
options.args = [ options.args = [
...(defaultBrowserOptions.args || []), ...(defaultBrowserOptions.args || []),
`--user-data-dir=${userDataDir}` `--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 // This might throw. See https://github.com/puppeteer/puppeteer/issues/2778
await rmAsync(userDataDir).catch(e => {}); 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 userDataDir = await mkdtempAsync(TMP_FOLDER);
const options = Object.assign({userDataDir}, defaultBrowserOptions); const options = Object.assign({userDataDir}, defaultBrowserOptions);
const browser = await puppeteer.launch(options); const browser = await puppeteer.launch(options);
@ -204,7 +220,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await rmAsync(userDataDir).catch(e => {}); await rmAsync(userDataDir).catch(e => {});
}); });
// This mysteriously fails on Windows on AppVeyor. See https://github.com/puppeteer/puppeteer/issues/4111 // 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 userDataDir = await mkdtempAsync(TMP_FOLDER);
const options = Object.assign({userDataDir}, defaultBrowserOptions); const options = Object.assign({userDataDir}, defaultBrowserOptions);
const browser = await puppeteer.launch(options); const browser = await puppeteer.launch(options);
@ -222,12 +240,14 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await rmAsync(userDataDir).catch(e => {}); await rmAsync(userDataDir).catch(e => {});
}); });
it('should return the default arguments', async() => { 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('--no-first-run');
expect(puppeteer.defaultArgs()).toContain('--headless'); expect(puppeteer.defaultArgs()).toContain('--headless');
expect(puppeteer.defaultArgs({headless: false})).not.toContain('--headless'); expect(puppeteer.defaultArgs({headless: false})).not.toContain('--headless');
expect(puppeteer.defaultArgs({userDataDir: 'foo'})).toContain('--user-data-dir=foo'); 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('--headless');
expect(puppeteer.defaultArgs()).toContain('--no-remote'); expect(puppeteer.defaultArgs()).toContain('--no-remote');
expect(puppeteer.defaultArgs()).toContain('--foreground'); expect(puppeteer.defaultArgs()).toContain('--foreground');
@ -242,12 +262,14 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
} }
}); });
it('should report the correct product', async() => { it('should report the correct product', async() => {
if (CHROME) const {isChrome, isFirefox, puppeteer} = getTestState();
if (isChrome)
expect(puppeteer.product).toBe('chrome'); expect(puppeteer.product).toBe('chrome');
else if (FFOX) else if (isFirefox)
expect(puppeteer.product).toBe('firefox'); 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); const options = Object.assign({}, defaultBrowserOptions);
options.ignoreDefaultArgs = true; options.ignoreDefaultArgs = true;
const browser = await puppeteer.launch(options); const browser = await puppeteer.launch(options);
@ -256,7 +278,8 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await page.close(); await page.close();
await browser.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. // Make sure we launch with `--enable-automation` by default.
const defaultArgs = puppeteer.defaultArgs(); const defaultArgs = puppeteer.defaultArgs();
const browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { const browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
@ -270,12 +293,15 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await browser.close(); await browser.close();
}); });
it('should have default URL when launching browser', async function() { it('should have default URL when launching browser', async function() {
const {defaultBrowserOptions, puppeteer} = getTestState();
const browser = await puppeteer.launch(defaultBrowserOptions); const browser = await puppeteer.launch(defaultBrowserOptions);
const pages = (await browser.pages()).map(page => page.url()); const pages = (await browser.pages()).map(page => page.url());
expect(pages).toEqual(['about:blank']); expect(pages).toEqual(['about:blank']);
await browser.close(); 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); const options = Object.assign({}, defaultBrowserOptions);
options.args = [server.EMPTY_PAGE].concat(options.args || []); options.args = [server.EMPTY_PAGE].concat(options.args || []);
const browser = await puppeteer.launch(options); const browser = await puppeteer.launch(options);
@ -288,6 +314,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await browser.close(); await browser.close();
}); });
it('should set the default viewport', async() => { it('should set the default viewport', async() => {
const {puppeteer, defaultBrowserOptions} = getTestState();
const options = Object.assign({}, defaultBrowserOptions, { const options = Object.assign({}, defaultBrowserOptions, {
defaultViewport: { defaultViewport: {
width: 456, width: 456,
@ -301,6 +328,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await browser.close(); await browser.close();
}); });
it('should disable the default viewport', async() => { it('should disable the default viewport', async() => {
const {puppeteer, defaultBrowserOptions} = getTestState();
const options = Object.assign({}, defaultBrowserOptions, { const options = Object.assign({}, defaultBrowserOptions, {
defaultViewport: null defaultViewport: null
}); });
@ -309,7 +337,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
expect(page.viewport()).toBe(null); expect(page.viewport()).toBe(null);
await browser.close(); 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, { const options = Object.assign({}, defaultBrowserOptions, {
defaultViewport: null defaultViewport: null
}); });
@ -324,20 +354,24 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
}); });
}); });
describe('Puppeteer.connect', function() { 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 originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browser = await puppeteer.connect({ const otherBrowser = await puppeteer.connect({
browserWSEndpoint: originalBrowser.wsEndpoint() browserWSEndpoint: originalBrowser.wsEndpoint()
}); });
const page = await browser.newPage(); const page = await otherBrowser.newPage();
expect(await page.evaluate(() => 7 * 8)).toBe(56); expect(await page.evaluate(() => 7 * 8)).toBe(56);
browser.disconnect(); otherBrowser.disconnect();
const secondPage = await originalBrowser.newPage(); const secondPage = await originalBrowser.newPage();
expect(await secondPage.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work'); expect(await secondPage.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
await originalBrowser.close(); 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 originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const remoteBrowser = await puppeteer.connect({ const remoteBrowser = await puppeteer.connect({
browserWSEndpoint: originalBrowser.wsEndpoint() browserWSEndpoint: originalBrowser.wsEndpoint()
@ -347,7 +381,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
remoteBrowser.close(), 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 originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browserWSEndpoint = originalBrowser.wsEndpoint(); const browserWSEndpoint = originalBrowser.wsEndpoint();
@ -366,7 +402,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await page.close(); await page.close();
await browser.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 originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browserWSEndpoint = originalBrowser.wsEndpoint(); const browserWSEndpoint = originalBrowser.wsEndpoint();
const page = await originalBrowser.newPage(); const page = await originalBrowser.newPage();
@ -387,7 +425,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
await browser.close(); await browser.close();
}); });
// @see https://github.com/puppeteer/puppeteer/issues/4197#issuecomment-481793410 // @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 browserOne = await puppeteer.launch();
const browserTwo = await puppeteer.connect({ browserWSEndpoint: browserOne.wsEndpoint() }); const browserTwo = await puppeteer.connect({ browserWSEndpoint: browserOne.wsEndpoint() });
const [page1, page2] = await Promise.all([ const [page1, page2] = await Promise.all([
@ -401,7 +441,9 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
}); });
describe('Puppeteer.executablePath', function() { describe('Puppeteer.executablePath', function() {
it_fails_ffox('should work', async({server}) => { itFailsFirefox('should work', async() => {
const { puppeteer } = getTestState();
const executablePath = puppeteer.executablePath(); const executablePath = puppeteer.executablePath();
expect(fs.existsSync(executablePath)).toBe(true); expect(fs.existsSync(executablePath)).toBe(true);
expect(fs.realpathSync(executablePath)).toBe(executablePath); expect(fs.realpathSync(executablePath)).toBe(executablePath);
@ -411,17 +453,21 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
describe('Top-level requires', function() { describe('Top-level requires', function() {
it('should require top-level Errors', async() => { it('should require top-level Errors', async() => {
const {puppeteer, puppeteerPath} = getTestState();
const Errors = require(path.join(puppeteerPath, '/Errors')); const Errors = require(path.join(puppeteerPath, '/Errors'));
expect(Errors.TimeoutError).toBe(puppeteer.errors.TimeoutError); expect(Errors.TimeoutError).toBe(puppeteer.errors.TimeoutError);
}); });
it('should require top-level DeviceDescriptors', async() => { it('should require top-level DeviceDescriptors', async() => {
const {puppeteer, puppeteerPath} = getTestState();
const Devices = require(path.join(puppeteerPath, '/DeviceDescriptors')); const Devices = require(path.join(puppeteerPath, '/DeviceDescriptors'));
expect(Devices['iPhone 6']).toBe(puppeteer.devices['iPhone 6']); expect(Devices['iPhone 6']).toBe(puppeteer.devices['iPhone 6']);
}); });
}); });
describe('Browser target events', function() { 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 browser = await puppeteer.launch(defaultBrowserOptions);
const events = []; const events = [];
browser.on('targetcreated', () => events.push('CREATED')); browser.on('targetcreated', () => events.push('CREATED'));
@ -437,6 +483,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
describe('Browser.Events.disconnected', function() { describe('Browser.Events.disconnected', function() {
it('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async() => { 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 originalBrowser = await puppeteer.launch(defaultBrowserOptions);
const browserWSEndpoint = originalBrowser.wsEndpoint(); const browserWSEndpoint = originalBrowser.wsEndpoint();
const remoteBrowser1 = await puppeteer.connect({browserWSEndpoint}); const remoteBrowser1 = await puppeteer.connect({browserWSEndpoint});
@ -469,5 +516,4 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
expect(disconnectedRemote2).toBe(1); expect(disconnectedRemote2).toBe(1);
}); });
}); });
});
};

155
test/mocha-utils.js Normal file
View File

@ -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;
});

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
const os = require('os'); const os = require('os');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
function dimensions() { function dimensions() {
const rect = document.querySelector('textarea').getBoundingClientRect(); const rect = document.querySelector('textarea').getBoundingClientRect();
@ -25,132 +27,144 @@ function dimensions() {
}; };
} }
module.exports.addTests = function({testRunner, expect, FFOX}) { describe('Mouse', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; it('should click the document', async() => {
const { page } = getTestState();
describe('Mouse', function() { await page.evaluate(() => {
it('should click the document', async({page, server}) => { window.clickPromise = new Promise(resolve => {
await page.evaluate(() => { document.addEventListener('click', event => {
window.clickPromise = new Promise(resolve => { resolve({
document.addEventListener('click', event => { type: event.type,
resolve({ detail: event.detail,
type: event.type, clientX: event.clientX,
detail: event.detail, clientY: event.clientY,
clientX: event.clientX, isTrusted: event.isTrusted,
clientY: event.clientY, button: event.button
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});
});
});

View File

@ -15,18 +15,22 @@
*/ */
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) { describe('navigation', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.goto', function() { describe('Page.goto', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(page.url()).toBe(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); await page.goto(server.EMPTY_PAGE);
expect(page.url()).toBe(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE);
await page.goto(server.EMPTY_PAGE + '#foo'); 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'); await page.goto(server.EMPTY_PAGE + '#bar');
expect(page.url()).toBe(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/1.html', '/redirect/2.html');
server.setRedirect('/redirect/2.html', '/empty.html'); server.setRedirect('/redirect/2.html', '/empty.html');
await page.goto(server.PREFIX + '/redirect/1.html'); await page.goto(server.PREFIX + '/redirect/1.html');
expect(page.url()).toBe(server.EMPTY_PAGE); 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'); const response = await page.goto('about:blank');
expect(response).toBe(null); 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'); const response = await page.goto(server.PREFIX + '/historyapi.html');
expect(response.status()).toBe(200); 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) => { server.setRoute('/frames/frame.html', (req, res) => {
res.statusCode = 204; res.statusCode = 204;
res.end(); res.end();
}); });
await page.goto(server.PREFIX + '/frames/one-frame.html'); 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) => { server.setRoute('/empty.html', (req, res) => {
res.statusCode = 204; res.statusCode = 204;
res.end(); res.end();
@ -63,16 +77,20 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
let error = null; let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e); await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).not.toBe(null); expect(error).not.toBe(null);
if (CHROME) if (isChrome)
expect(error.message).toContain('net::ERR_ABORTED'); expect(error.message).toContain('net::ERR_ABORTED');
else else
expect(error.message).toContain('NS_BINDING_ABORTED'); 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'}); const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
expect(response.status()).toBe(200); 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false); 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'); const response = await page.goto(server.PREFIX + '/grid.html');
expect(response.status()).toBe(200); 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'}); const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle0'});
expect(response.status()).toBe(200); 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'}); const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle2'});
expect(response.status()).toBe(200); 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; let error = null;
await page.goto('asdfasdf').catch(e => error = e); await page.goto('asdfasdf').catch(e => error = e);
if (CHROME) if (isChrome)
expect(error.message).toContain('Cannot navigate to invalid URL'); expect(error.message).toContain('Cannot navigate to invalid URL');
else else
expect(error.message).toContain('Invalid url'); 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'. // Make sure that network events do not emit 'undefined'.
// @see https://crbug.com/750469 // @see https://crbug.com/750469
page.on('request', request => expect(request).toBeTruthy()); 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()); page.on('requestfailed', request => expect(request).toBeTruthy());
let error = null; let error = null;
await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e); await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
if (CHROME) if (isChrome)
expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID'); expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID');
else else
expect(error.message).toContain('SSL_ERROR_UNKNOWN'); 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/1.html', '/redirect/2.html');
server.setRedirect('/redirect/2.html', '/empty.html'); server.setRedirect('/redirect/2.html', '/empty.html');
let error = null; let error = null;
await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e); 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'); expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID');
else else
expect(error.message).toContain('SSL_ERROR_UNKNOWN'); 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; let error = null;
await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle'}).catch(err => error = err); await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle'}).catch(err => error = err);
expect(error.message).toContain('"networkidle" option is no longer supported'); 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; let error = null;
await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e); 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'); expect(error.message).toContain('net::ERR_CONNECTION_REFUSED');
else else
expect(error.message).toContain('NS_ERROR_CONNECTION_REFUSED'); 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 // Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { }); server.setRoute('/empty.html', (req, res) => { });
let error = null; 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.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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 // Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { }); server.setRoute('/empty.html', (req, res) => { });
let error = null; 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.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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 // Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { }); server.setRoute('/empty.html', (req, res) => { });
let error = null; 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.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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 // Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { }); server.setRoute('/empty.html', (req, res) => { });
let error = null; 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.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); 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 error = null;
let loaded = false; let loaded = false;
page.once('load', () => loaded = true); page.once('load', () => loaded = true);
@ -176,20 +218,28 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(error).toBe(null); expect(error).toBe(null);
expect(loaded).toBe(true); 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); const response = await page.goto(server.EMPTY_PAGE);
expect(response.ok()).toBe(true); 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'); const response = await page.goto('data:text/html,hello');
expect(response.ok()).toBe(true); 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'); const response = await page.goto(server.PREFIX + '/not-found');
expect(response.ok()).toBe(false); expect(response.ok()).toBe(false);
expect(response.status()).toBe(404); 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/1.html', '/redirect/2.html');
server.setRedirect('/redirect/2.html', '/redirect/3.html'); server.setRedirect('/redirect/2.html', '/redirect/3.html');
server.setRedirect('/redirect/3.html', server.EMPTY_PAGE); 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.ok()).toBe(true);
expect(response.url()).toBe(server.EMPTY_PAGE); 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 = []; let responses = [];
// Hold on to a bunch of requests without answering. // Hold on to a bunch of requests without answering.
server.setRoute('/fetch-request-a.js', (req, res) => responses.push(res)); 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 navigation to succeed.
expect(response.ok()).toBe(true); 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; let warning = null;
const warningHandler = w => warning = w; const warningHandler = w => warning = w;
process.on('warning', warningHandler); process.on('warning', warningHandler);
@ -263,7 +317,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
process.removeListener('warning', warningHandler); process.removeListener('warning', warningHandler);
expect(warning).toBe(null); 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; let warning = null;
const warningHandler = w => warning = w; const warningHandler = w => warning = w;
process.on('warning', warningHandler); process.on('warning', warningHandler);
@ -272,7 +328,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
process.removeListener('warning', warningHandler); process.removeListener('warning', warningHandler);
expect(warning).toBe(null); 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; let warning = null;
const warningHandler = w => warning = w; const warningHandler = w => warning = w;
process.on('warning', warningHandler); process.on('warning', warningHandler);
@ -284,7 +342,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
process.removeListener('warning', warningHandler); process.removeListener('warning', warningHandler);
expect(warning).toBe(null); 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 = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); page.on('request', request => !utils.isFavicon(request) && requests.push(request));
const dataURL = 'data:text/html,<div>yo</div>'; const dataURL = 'data:text/html,<div>yo</div>';
@ -293,7 +353,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(dataURL); 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 = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); page.on('request', request => !utils.isFavicon(request) && requests.push(request));
const response = await page.goto(server.EMPTY_PAGE + '#hash'); 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.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE); 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'); const response = await page.goto(server.PREFIX + '/self-request.html');
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
expect(response.url()).toContain('self-request.html'); 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'; const url = httpsServer.PREFIX + '/redirect/1.html';
let error = null; let error = null;
try { try {
@ -317,7 +383,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
} }
expect(error.message).toContain(url); 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([ const [request1, request2] = await Promise.all([
server.waitForRequest('/grid.html'), server.waitForRequest('/grid.html'),
server.waitForRequest('/digits/1.png'), server.waitForRequest('/digits/1.png'),
@ -332,7 +400,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
}); });
describe('Page.waitForNavigation', function() { 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); await page.goto(server.EMPTY_PAGE);
const [response] = await Promise.all([ const [response] = await Promise.all([
page.waitForNavigation(), page.waitForNavigation(),
@ -341,7 +411,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
expect(response.url()).toContain('grid.html'); 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; let response = null;
server.setRoute('/one-style.css', (req, res) => response = res); server.setRoute('/one-style.css', (req, res) => response = res);
const navigationPromise = page.goto(server.PREFIX + '/one-style.html'); const navigationPromise = page.goto(server.PREFIX + '/one-style.html');
@ -361,7 +433,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
await bothFiredPromise; await bothFiredPromise;
await navigationPromise; 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.goto(server.EMPTY_PAGE);
await page.setContent(`<a href='#foobar'>foobar</a>`); await page.setContent(`<a href='#foobar'>foobar</a>`);
const [response] = await Promise.all([ const [response] = await Promise.all([
@ -371,7 +445,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response).toBe(null); expect(response).toBe(null);
expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar'); 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.goto(server.EMPTY_PAGE);
await page.setContent(` await page.setContent(`
<a onclick='javascript:pushState()'>SPA</a> <a onclick='javascript:pushState()'>SPA</a>
@ -386,7 +462,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response).toBe(null); expect(response).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/wow.html'); 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.goto(server.EMPTY_PAGE);
await page.setContent(` await page.setContent(`
<a onclick='javascript:replaceState()'>SPA</a> <a onclick='javascript:replaceState()'>SPA</a>
@ -401,7 +479,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response).toBe(null); expect(response).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/replaced.html'); 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.goto(server.EMPTY_PAGE);
await page.setContent(` await page.setContent(`
<a id=back onclick='javascript:goBack()'>back</a> <a id=back onclick='javascript:goBack()'>back</a>
@ -427,7 +507,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(forwardResponse).toBe(null); expect(forwardResponse).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/second.html'); 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) => {}); server.setRoute('/frames/style.css', (req, res) => {});
const navigationPromise = page.goto(server.PREFIX + '/frames/one-frame.html'); const navigationPromise = page.goto(server.PREFIX + '/frames/one-frame.html');
const frame = await utils.waitEvent(page, 'frameattached'); 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() { describeFailsFirefox('Page.goBack', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
@ -460,7 +544,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
response = await page.goForward(); response = await page.goForward();
expect(response).toBe(null); 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {
history.pushState({}, '', '/first.html'); history.pushState({}, '', '/first.html');
@ -477,8 +563,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
}); });
}); });
describe_fails_ffox('Frame.goto', function() { describeFailsFirefox('Frame.goto', function() {
it('should navigate subframes', async({page, server}) => { it('should navigate subframes', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html'); await page.goto(server.PREFIX + '/frames/one-frame.html');
expect(page.frames()[0].url()).toContain('/frames/one-frame.html'); expect(page.frames()[0].url()).toContain('/frames/one-frame.html');
expect(page.frames()[1].url()).toContain('/frames/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.ok()).toBe(true);
expect(response.frame()).toBe(page.frames()[1]); 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'); await page.goto(server.PREFIX + '/frames/one-frame.html');
server.setRoute('/empty.html', () => {}); server.setRoute('/empty.html', () => {});
@ -498,7 +588,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
const error = await navigationPromise; const error = await navigationPromise;
expect(error.message).toBe('Navigating frame was detached'); 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. // Disable cache: otherwise, chromium will cache similar requests.
await page.setCacheEnabled(false); await page.setCacheEnabled(false);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -527,8 +619,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
}); });
}); });
describe_fails_ffox('Frame.waitForNavigation', function() { describeFailsFirefox('Frame.waitForNavigation', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html'); await page.goto(server.PREFIX + '/frames/one-frame.html');
const frame = page.frames()[1]; const frame = page.frames()[1];
const [response] = await Promise.all([ const [response] = await Promise.all([
@ -540,7 +634,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response.frame()).toBe(frame); expect(response.frame()).toBe(frame);
expect(page.url()).toContain('/frames/one-frame.html'); 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'); await page.goto(server.PREFIX + '/frames/one-frame.html');
const frame = page.frames()[1]; const frame = page.frames()[1];
@ -558,11 +654,13 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
}); });
describe('Page.reload', function() { 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.goto(server.EMPTY_PAGE);
await page.evaluate(() => window._foo = 10); await page.evaluate(() => window._foo = 10);
await page.reload(); await page.reload();
expect(await page.evaluate(() => window._foo)).toBe(undefined); expect(await page.evaluate(() => window._foo)).toBe(undefined);
}); });
}); });
}; });

View File

@ -17,27 +17,34 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, CHROME}) { describe('network', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.Events.Request', function() { 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 = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); page.on('request', request => !utils.isFavicon(request) && requests.push(request));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1); 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 = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); page.on('request', request => !utils.isFavicon(request) && requests.push(request));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(2); 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 = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); page.on('request', request => !utils.isFavicon(request) && requests.push(request));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -47,14 +54,18 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
describe('Request.frame', function() { 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 = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); page.on('request', request => !utils.isFavicon(request) && requests.push(request));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame()); 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); await page.goto(server.EMPTY_PAGE);
const requests = []; const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); 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.length).toBe(1);
expect(requests[0].frame()).toBe(page.frames()[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); await page.goto(server.EMPTY_PAGE);
let requests = []; let requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request)); 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() { describeFailsFirefox('Request.headers', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server, isChrome } = getTestState();
const response = await page.goto(server.EMPTY_PAGE); const response = await page.goto(server.EMPTY_PAGE);
if (CHROME) if (isChrome)
expect(response.request().headers()['user-agent']).toContain('Chrome'); expect(response.request().headers()['user-agent']).toContain('Chrome');
else else
expect(response.request().headers()['user-agent']).toContain('Firefox'); expect(response.request().headers()['user-agent']).toContain('Firefox');
}); });
}); });
describe_fails_ffox('Response.headers', function() { describeFailsFirefox('Response.headers', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
server.setRoute('/empty.html', (req, res) => { server.setRoute('/empty.html', (req, res) => {
res.setHeader('foo', 'bar'); res.setHeader('foo', 'bar');
res.end(); res.end();
@ -94,13 +111,17 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Response.fromCache', function() { describeFailsFirefox('Response.fromCache', function() {
it('should return |false| for non-cached content', async({page, server}) => { it('should return |false| for non-cached content', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE); const response = await page.goto(server.EMPTY_PAGE);
expect(response.fromCache()).toBe(false); expect(response.fromCache()).toBe(false);
}); });
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
const responses = new Map(); const responses = new Map();
page.on('response', r => !utils.isFavicon(r.request()) && responses.set(r.url().split('/').pop(), r)); 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() { describeFailsFirefox('Response.fromServiceWorker', function() {
it('should return |false| for non-service-worker content', async({page, server}) => { it('should return |false| for non-service-worker content', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE); const response = await page.goto(server.EMPTY_PAGE);
expect(response.fromServiceWorker()).toBe(false); expect(response.fromServiceWorker()).toBe(false);
}); });
it('Response.fromServiceWorker', async({page, server}) => { it('Response.fromServiceWorker', async() => {
const { page, server } = getTestState();
const responses = new Map(); const responses = new Map();
page.on('response', r => responses.set(r.url().split('/').pop(), r)); 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() { describeFailsFirefox('Request.postData', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end()); server.setRoute('/post', (req, res) => res.end());
let request = null; let request = null;
@ -149,24 +176,32 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(request).toBeTruthy(); expect(request).toBeTruthy();
expect(request.postData()).toBe('{"foo":"bar"}'); 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); const response = await page.goto(server.EMPTY_PAGE);
expect(response.request().postData()).toBe(undefined); expect(response.request().postData()).toBe(undefined);
}); });
}); });
describe_fails_ffox('Response.text', function() { describeFailsFirefox('Response.text', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.PREFIX + '/simple.json'); const response = await page.goto(server.PREFIX + '/simple.json');
expect(await response.text()).toBe('{"foo": "bar"}\n'); 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'); server.enableGzip('/simple.json');
const response = await page.goto(server.PREFIX + '/simple.json'); const response = await page.goto(server.PREFIX + '/simple.json');
expect(response.headers()['content-encoding']).toBe('gzip'); expect(response.headers()['content-encoding']).toBe('gzip');
expect(await response.text()).toBe('{"foo": "bar"}\n'); 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'); server.setRedirect('/foo.html', '/empty.html');
const response = await page.goto(server.PREFIX + '/foo.html'); const response = await page.goto(server.PREFIX + '/foo.html');
const redirectChain = response.request().redirectChain(); const redirectChain = response.request().redirectChain();
@ -177,7 +212,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await redirected.text().catch(e => error = e); await redirected.text().catch(e => error = e);
expect(error.message).toContain('Response body is unavailable for redirect responses'); 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); await page.goto(server.EMPTY_PAGE);
// Setup server to trap request. // Setup server to trap request.
let serverResponse = null; let serverResponse = null;
@ -212,21 +249,27 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Response.json', function() { describeFailsFirefox('Response.json', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.PREFIX + '/simple.json'); const response = await page.goto(server.PREFIX + '/simple.json');
expect(await response.json()).toEqual({foo: 'bar'}); expect(await response.json()).toEqual({foo: 'bar'});
}); });
}); });
describe_fails_ffox('Response.buffer', function() { describeFailsFirefox('Response.buffer', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.PREFIX + '/pptr.png'); const response = await page.goto(server.PREFIX + '/pptr.png');
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
const responseBuffer = await response.buffer(); const responseBuffer = await response.buffer();
expect(responseBuffer.equals(imageBuffer)).toBe(true); 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'); server.enableGzip('/pptr.png');
const response = await page.goto(server.PREFIX + '/pptr.png'); const response = await page.goto(server.PREFIX + '/pptr.png');
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', '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() { describeFailsFirefox('Response.statusText', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
server.setRoute('/cool', (req, res) => { server.setRoute('/cool', (req, res) => {
res.writeHead(200, 'cool!'); res.writeHead(200, 'cool!');
res.end(); res.end();
@ -246,8 +291,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Network Events', function() { describeFailsFirefox('Network Events', function() {
it('Page.Events.Request', async({page, server}) => { it('Page.Events.Request', async() => {
const { page, server } = getTestState();
const requests = []; const requests = [];
page.on('request', request => requests.push(request)); page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE); 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() === page.mainFrame()).toBe(true);
expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); 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 = []; const responses = [];
page.on('response', response => responses.push(response)); page.on('response', response => responses.push(response));
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -274,7 +323,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(remoteAddress.port).toBe(server.PORT); 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); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
if (request.url().endsWith('css')) 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].url()).toContain('one-style.css');
expect(failedRequests[0].response()).toBe(null); expect(failedRequests[0].response()).toBe(null);
expect(failedRequests[0].resourceType()).toBe('stylesheet'); expect(failedRequests[0].resourceType()).toBe('stylesheet');
if (CHROME) if (isChrome)
expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED'); expect(failedRequests[0].failure().errorText).toBe('net::ERR_FAILED');
else else
expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE'); expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_FAILURE');
expect(failedRequests[0].frame()).toBeTruthy(); expect(failedRequests[0].frame()).toBeTruthy();
}); });
it('Page.Events.RequestFinished', async({page, server}) => { it('Page.Events.RequestFinished', async() => {
const { page, server } = getTestState();
const requests = []; const requests = [];
page.on('requestfinished', request => requests.push(request)); page.on('requestfinished', request => requests.push(request));
await page.goto(server.EMPTY_PAGE); 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() === page.mainFrame()).toBe(true);
expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE); 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 = []; const events = [];
page.on('request', request => events.push('request')); page.on('request', request => events.push('request'));
page.on('response', response => events.push('response')); page.on('response', response => events.push('response'));
@ -313,7 +368,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(events).toEqual(['request', 'response', 'requestfinished']); expect(events).toEqual(['request', 'response', 'requestfinished']);
}); });
it('should support redirects', async({page, server}) => { it('should support redirects', async() => {
const { page, server } = getTestState();
const events = []; const events = [];
page.on('request', request => events.push(`${request.method()} ${request.url()}`)); page.on('request', request => events.push(`${request.method()} ${request.url()}`));
page.on('response', response => events.push(`${response.status()} ${response.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', () => { describe('Request.isNavigationRequest', () => {
it_fails_ffox('should work', async({page, server}) => { itFailsFirefox('should work', async() => {
const { page, server } = getTestState();
const requests = new Map(); const requests = new Map();
page.on('request', request => requests.set(request.url().split('/').pop(), request)); page.on('request', request => requests.set(request.url().split('/').pop(), request));
server.setRedirect('/rrredirect', '/frames/one-frame.html'); 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('script.js').isNavigationRequest()).toBe(false);
expect(requests.get('style.css').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(); const requests = new Map();
page.on('request', request => { page.on('request', request => {
requests.set(request.url().split('/').pop(), 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('script.js').isNavigationRequest()).toBe(false);
expect(requests.get('style.css').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 = []; const requests = [];
page.on('request', request => requests.push(request)); page.on('request', request => requests.push(request));
await page.goto(server.PREFIX + '/pptr.png'); await page.goto(server.PREFIX + '/pptr.png');
@ -374,8 +437,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Page.setExtraHTTPHeaders', function() { describeFailsFirefox('Page.setExtraHTTPHeaders', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.setExtraHTTPHeaders({ await page.setExtraHTTPHeaders({
foo: 'bar' foo: 'bar'
}); });
@ -385,7 +450,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]); ]);
expect(request.headers['foo']).toBe('bar'); 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; let error = null;
try { try {
await page.setExtraHTTPHeaders({ 'foo': 1 }); await page.setExtraHTTPHeaders({ 'foo': 1 });
@ -396,8 +463,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Page.authenticate', function() { describeFailsFirefox('Page.authenticate', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
server.setAuth('/empty.html', 'user', 'pass'); server.setAuth('/empty.html', 'user', 'pass');
let response = await page.goto(server.EMPTY_PAGE); let response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(401); expect(response.status()).toBe(401);
@ -408,7 +477,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
response = await page.reload(); response = await page.reload();
expect(response.status()).toBe(200); 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. // Use unique user/password since Chrome caches credentials per origin.
server.setAuth('/empty.html', 'user2', 'pass2'); server.setAuth('/empty.html', 'user2', 'pass2');
await page.authenticate({ await page.authenticate({
@ -418,7 +489,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const response = await page.goto(server.EMPTY_PAGE); const response = await page.goto(server.EMPTY_PAGE);
expect(response.status()).toBe(401); 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. // Use unique user/password since Chrome caches credentials per origin.
server.setAuth('/empty.html', 'user3', 'pass3'); server.setAuth('/empty.html', 'user3', 'pass3');
await page.authenticate({ await page.authenticate({
@ -433,5 +506,5 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.status()).toBe(401); expect(response.status()).toBe(401);
}); });
}); });
};
});

View File

@ -14,43 +14,53 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { const expect = require('expect');
const {describe, xdescribe, fdescribe} = testRunner; const {getTestState} = require('./mocha-utils');
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('OOPIF', function() { describeChromeOnly('OOPIF', function() {
beforeAll(async function(state) { /* We use a special browser for this test as we need the --site-per-process flag */
state.browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, { let browser;
args: (defaultBrowserOptions.args || []).concat(['--site-per-process']), let context;
})); let page;
});
beforeEach(async function(state) { before(async() => {
state.context = await state.browser.createIncognitoBrowserContext(); const {puppeteer, defaultBrowserOptions} = getTestState();
state.page = await state.context.newPage(); browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
}); args: (defaultBrowserOptions.args || []).concat(['--site-per-process']),
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);
});
}); });
};
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);
});
});
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -1,173 +0,0 @@
/**
* Copyright 2019 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 fs = require('fs');
const path = require('path');
const rm = require('rimraf').sync;
const utils = require('./utils');
const expect = require('expect');
const YELLOW_COLOR = '\x1b[33m';
const RESET_COLOR = '\x1b[0m';
module.exports.addTests = ({testRunner, product, puppeteerPath}) => {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const CHROME = product === 'Chromium';
const FFOX = product === 'Firefox';
const puppeteer = require(puppeteerPath);
const headless = (process.env.HEADLESS || 'true').trim().toLowerCase() === 'true';
const slowMo = parseInt((process.env.SLOW_MO || '0').trim(), 10);
let extraLaunchOptions = {};
try {
extraLaunchOptions = JSON.parse(process.env.EXTRA_LAUNCH_OPTIONS || '{}');
} catch (error) {
console.warn(`${YELLOW_COLOR}Error parsing EXTRA_LAUNCH_OPTIONS: ${error.message}. Skipping.${RESET_COLOR}`);
}
const defaultBrowserOptions = Object.assign({
handleSIGINT: false,
executablePath: process.env.BINARY,
slowMo,
headless,
dumpio: !!process.env.DUMPIO,
}, extraLaunchOptions);
if (defaultBrowserOptions.executablePath) {
console.warn(`${YELLOW_COLOR}WARN: running ${product} tests with ${defaultBrowserOptions.executablePath}${RESET_COLOR}`);
} 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 suffix = FFOX ? 'firefox' : 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);
const testOptions = {
testRunner,
product,
FFOX,
CHROME,
puppeteer,
expect,
defaultBrowserOptions,
puppeteerPath,
headless: !!defaultBrowserOptions.headless,
};
describe('Browser', function() {
beforeAll(async state => {
state.browser = await puppeteer.launch(defaultBrowserOptions);
});
afterAll(async state => {
await state.browser.close();
state.browser = null;
});
beforeEach(async(state, test) => {
const rl = require('readline').createInterface({input: state.browser.process().stderr});
test.output = '';
rl.on('line', onLine);
state.tearDown = () => {
rl.removeListener('line', onLine);
rl.close();
};
function onLine(line) {
test.output += line + '\n';
}
});
afterEach(async state => {
state.tearDown();
});
describe('Page', function() {
beforeEach(async state => {
state.context = await state.browser.createIncognitoBrowserContext();
state.page = await state.context.newPage();
});
afterEach(async state => {
// This closes all pages.
await state.context.close();
state.context = null;
state.page = null;
});
// Page-level tests that are given a browser, a context and a page.
// Each test is launched in a new browser context.
require('./accessibility.spec.js').addTests(testOptions);
require('./browser.spec.js').addTests(testOptions);
require('./click.spec.js').addTests(testOptions);
require('./cookies.spec.js').addTests(testOptions);
require('./dialog.spec.js').addTests(testOptions);
require('./elementhandle.spec.js').addTests(testOptions);
require('./emulation.spec.js').addTests(testOptions);
require('./evaluation.spec.js').addTests(testOptions);
require('./frame.spec.js').addTests(testOptions);
require('./input.spec.js').addTests(testOptions);
require('./jshandle.spec.js').addTests(testOptions);
require('./keyboard.spec.js').addTests(testOptions);
require('./mouse.spec.js').addTests(testOptions);
require('./navigation.spec.js').addTests(testOptions);
require('./network.spec.js').addTests(testOptions);
require('./requestinterception.spec.js').addTests(testOptions);
require('./page.spec.js').addTests(testOptions);
require('./screenshot.spec.js').addTests(testOptions);
require('./queryselector.spec.js').addTests(testOptions);
require('./target.spec.js').addTests(testOptions);
require('./touchscreen.spec.js').addTests(testOptions);
require('./waittask.spec.js').addTests(testOptions);
require('./worker.spec.js').addTests(testOptions);
if (CHROME) {
require('./CDPSession.spec.js').addTests(testOptions);
require('./coverage.spec.js').addTests(testOptions);
// Add page-level Chromium-specific tests.
require('./chromiumonly.spec.js').addPageTests(testOptions);
}
});
// Browser-level tests that are given a browser.
require('./browsercontext.spec.js').addTests(testOptions);
});
// Top-level tests that launch Browser themselves.
require('./ignorehttpserrors.spec.js').addTests(testOptions);
require('./defaultbrowsercontext.spec.js').addTests(testOptions);
require('./launcher.spec.js').addTests(testOptions);
require('./fixtures.spec.js').addTests(testOptions);
if (CHROME) {
require('./oopif.spec.js').addTests(testOptions);
require('./headful.spec.js').addTests(testOptions);
require('./tracing.spec.js').addTests(testOptions);
// Add top-level Chromium-specific tests.
require('./chromiumonly.spec.js').addLauncherTests(testOptions);
}
};

View File

@ -13,83 +13,107 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, product}) { describe('querySelector', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describeFailsFirefox('Page.$eval', function() {
it('should work', async() => {
const { page } = getTestState();
describe_fails_ffox('Page.$eval', function() {
it('should work', async({page, server}) => {
await page.setContent('<section id="testAttribute">43543</section>'); await page.setContent('<section id="testAttribute">43543</section>');
const idAttribute = await page.$eval('section', e => e.id); const idAttribute = await page.$eval('section', e => e.id);
expect(idAttribute).toBe('testAttribute'); expect(idAttribute).toBe('testAttribute');
}); });
it('should accept arguments', async({page, server}) => { it('should accept arguments', async() => {
const { page } = getTestState();
await page.setContent('<section>hello</section>'); await page.setContent('<section>hello</section>');
const text = await page.$eval('section', (e, suffix) => e.textContent + suffix, ' world!'); const text = await page.$eval('section', (e, suffix) => e.textContent + suffix, ' world!');
expect(text).toBe('hello world!'); expect(text).toBe('hello world!');
}); });
it('should accept ElementHandles as arguments', async({page, server}) => { it('should accept ElementHandles as arguments', async() => {
const { page } = getTestState();
await page.setContent('<section>hello</section><div> world</div>'); await page.setContent('<section>hello</section><div> world</div>');
const divHandle = await page.$('div'); const divHandle = await page.$('div');
const text = await page.$eval('section', (e, div) => e.textContent + div.textContent, divHandle); const text = await page.$eval('section', (e, div) => e.textContent + div.textContent, divHandle);
expect(text).toBe('hello world'); expect(text).toBe('hello world');
}); });
it('should throw error if no element is found', async({page, server}) => { it('should throw error if no element is found', async() => {
const { page } = getTestState();
let error = null; let error = null;
await page.$eval('section', e => e.id).catch(e => error = e); await page.$eval('section', e => e.id).catch(e => error = e);
expect(error.message).toContain('failed to find element matching selector "section"'); expect(error.message).toContain('failed to find element matching selector "section"');
}); });
}); });
describe_fails_ffox('Page.$$eval', function() { describeFailsFirefox('Page.$$eval', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
await page.setContent('<div>hello</div><div>beautiful</div><div>world!</div>'); await page.setContent('<div>hello</div><div>beautiful</div><div>world!</div>');
const divsCount = await page.$$eval('div', divs => divs.length); const divsCount = await page.$$eval('div', divs => divs.length);
expect(divsCount).toBe(3); expect(divsCount).toBe(3);
}); });
}); });
describe_fails_ffox('Page.$', function() { describeFailsFirefox('Page.$', function() {
it('should query existing element', async({page, server}) => { it('should query existing element', async() => {
const { page } = getTestState();
await page.setContent('<section>test</section>'); await page.setContent('<section>test</section>');
const element = await page.$('section'); const element = await page.$('section');
expect(element).toBeTruthy(); expect(element).toBeTruthy();
}); });
it('should return null for non-existing element', async({page, server}) => { it('should return null for non-existing element', async() => {
const { page } = getTestState();
const element = await page.$('non-existing-element'); const element = await page.$('non-existing-element');
expect(element).toBe(null); expect(element).toBe(null);
}); });
}); });
describe('Page.$$', function() { describe('Page.$$', function() {
it_fails_ffox('should query existing elements', async({page, server}) => { itFailsFirefox('should query existing elements', async() => {
const { page } = getTestState();
await page.setContent('<div>A</div><br/><div>B</div>'); await page.setContent('<div>A</div><br/><div>B</div>');
const elements = await page.$$('div'); const elements = await page.$$('div');
expect(elements.length).toBe(2); expect(elements.length).toBe(2);
const promises = elements.map(element => page.evaluate(e => e.textContent, element)); const promises = elements.map(element => page.evaluate(e => e.textContent, element));
expect(await Promise.all(promises)).toEqual(['A', 'B']); expect(await Promise.all(promises)).toEqual(['A', 'B']);
}); });
it('should return empty array if nothing is found', async({page, server}) => { it('should return empty array if nothing is found', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const elements = await page.$$('div'); const elements = await page.$$('div');
expect(elements.length).toBe(0); expect(elements.length).toBe(0);
}); });
}); });
describe_fails_ffox('Path.$x', function() { describeFailsFirefox('Path.$x', function() {
it('should query existing element', async({page, server}) => { it('should query existing element', async() => {
const { page } = getTestState();
await page.setContent('<section>test</section>'); await page.setContent('<section>test</section>');
const elements = await page.$x('/html/body/section'); const elements = await page.$x('/html/body/section');
expect(elements[0]).toBeTruthy(); expect(elements[0]).toBeTruthy();
expect(elements.length).toBe(1); expect(elements.length).toBe(1);
}); });
it('should return empty array for non-existing element', async({page, server}) => { it('should return empty array for non-existing element', async() => {
const { page } = getTestState();
const element = await page.$x('/html/body/non-existing-element'); const element = await page.$x('/html/body/non-existing-element');
expect(element).toEqual([]); expect(element).toEqual([]);
}); });
it('should return multiple elements', async({page, sever}) => { it('should return multiple elements', async() => {
const { page } = getTestState();
await page.setContent('<div></div><div></div>'); await page.setContent('<div></div><div></div>');
const elements = await page.$x('/html/body/div'); const elements = await page.$x('/html/body/div');
expect(elements.length).toBe(2); expect(elements.length).toBe(2);
@ -98,7 +122,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
describe('ElementHandle.$', function() { describe('ElementHandle.$', function() {
it('should query existing element', async({page, server}) => { it('should query existing element', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/playground.html'); await page.goto(server.PREFIX + '/playground.html');
await page.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>'); await page.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>');
const html = await page.$('html'); const html = await page.$('html');
@ -108,22 +134,28 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(content).toBe('A'); expect(content).toBe('A');
}); });
it_fails_ffox('should return null for non-existing element', async({page, server}) => { itFailsFirefox('should return null for non-existing element', async() => {
const { page } = getTestState();
await page.setContent('<html><body><div class="second"><div class="inner">B</div></div></body></html>'); await page.setContent('<html><body><div class="second"><div class="inner">B</div></div></body></html>');
const html = await page.$('html'); const html = await page.$('html');
const second = await html.$('.third'); const second = await html.$('.third');
expect(second).toBe(null); expect(second).toBe(null);
}); });
}); });
describe_fails_ffox('ElementHandle.$eval', function() { describeFailsFirefox('ElementHandle.$eval', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
await page.setContent('<html><body><div class="tweet"><div class="like">100</div><div class="retweets">10</div></div></body></html>'); await page.setContent('<html><body><div class="tweet"><div class="like">100</div><div class="retweets">10</div></div></body></html>');
const tweet = await page.$('.tweet'); const tweet = await page.$('.tweet');
const content = await tweet.$eval('.like', node => node.innerText); const content = await tweet.$eval('.like', node => node.innerText);
expect(content).toBe('100'); expect(content).toBe('100');
}); });
it('should retrieve content from subtree', async({page, server}) => { it('should retrieve content from subtree', async() => {
const { page } = getTestState();
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"><div class="a">a-child-div</div></div>'; const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"><div class="a">a-child-div</div></div>';
await page.setContent(htmlContent); await page.setContent(htmlContent);
const elementHandle = await page.$('#myId'); const elementHandle = await page.$('#myId');
@ -131,7 +163,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(content).toBe('a-child-div'); expect(content).toBe('a-child-div');
}); });
it('should throw in case of missing selector', async({page, server}) => { it('should throw in case of missing selector', async() => {
const { page } = getTestState();
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"></div>'; const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"></div>';
await page.setContent(htmlContent); await page.setContent(htmlContent);
const elementHandle = await page.$('#myId'); const elementHandle = await page.$('#myId');
@ -139,15 +173,19 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(errorMessage).toBe(`Error: failed to find element matching selector ".a"`); expect(errorMessage).toBe(`Error: failed to find element matching selector ".a"`);
}); });
}); });
describe_fails_ffox('ElementHandle.$$eval', function() { describeFailsFirefox('ElementHandle.$$eval', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page } = getTestState();
await page.setContent('<html><body><div class="tweet"><div class="like">100</div><div class="like">10</div></div></body></html>'); await page.setContent('<html><body><div class="tweet"><div class="like">100</div><div class="like">10</div></div></body></html>');
const tweet = await page.$('.tweet'); const tweet = await page.$('.tweet');
const content = await tweet.$$eval('.like', nodes => nodes.map(n => n.innerText)); const content = await tweet.$$eval('.like', nodes => nodes.map(n => n.innerText));
expect(content).toEqual(['100', '10']); expect(content).toEqual(['100', '10']);
}); });
it('should retrieve content from subtree', async({page, server}) => { it('should retrieve content from subtree', async() => {
const { page } = getTestState();
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"><div class="a">a1-child-div</div><div class="a">a2-child-div</div></div>'; const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"><div class="a">a1-child-div</div><div class="a">a2-child-div</div></div>';
await page.setContent(htmlContent); await page.setContent(htmlContent);
const elementHandle = await page.$('#myId'); const elementHandle = await page.$('#myId');
@ -155,7 +193,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(content).toEqual(['a1-child-div', 'a2-child-div']); expect(content).toEqual(['a1-child-div', 'a2-child-div']);
}); });
it('should not throw in case of missing selector', async({page, server}) => { it('should not throw in case of missing selector', async() => {
const { page } = getTestState();
const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"></div>'; const htmlContent = '<div class="a">not-a-child-div</div><div id="myId"></div>';
await page.setContent(htmlContent); await page.setContent(htmlContent);
const elementHandle = await page.$('#myId'); const elementHandle = await page.$('#myId');
@ -165,8 +205,10 @@ module.exports.addTests = function({testRunner, expect, product}) {
}); });
describe_fails_ffox('ElementHandle.$$', function() { describeFailsFirefox('ElementHandle.$$', function() {
it('should query existing elements', async({page, server}) => { it('should query existing elements', async() => {
const { page } = getTestState();
await page.setContent('<html><body><div>A</div><br/><div>B</div></body></html>'); await page.setContent('<html><body><div>A</div><br/><div>B</div></body></html>');
const html = await page.$('html'); const html = await page.$('html');
const elements = await html.$$('div'); const elements = await html.$$('div');
@ -175,7 +217,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(await Promise.all(promises)).toEqual(['A', 'B']); expect(await Promise.all(promises)).toEqual(['A', 'B']);
}); });
it('should return empty array for non-existing elements', async({page, server}) => { it('should return empty array for non-existing elements', async() => {
const { page } = getTestState();
await page.setContent('<html><body><span>A</span><br/><span>B</span></body></html>'); await page.setContent('<html><body><span>A</span><br/><span>B</span></body></html>');
const html = await page.$('html'); const html = await page.$('html');
const elements = await html.$$('div'); const elements = await html.$$('div');
@ -185,7 +229,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
describe('ElementHandle.$x', function() { describe('ElementHandle.$x', function() {
it('should query existing element', async({page, server}) => { it('should query existing element', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/playground.html'); await page.goto(server.PREFIX + '/playground.html');
await page.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>'); await page.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>');
const html = await page.$('html'); const html = await page.$('html');
@ -195,11 +241,13 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(content).toBe('A'); expect(content).toBe('A');
}); });
it_fails_ffox('should return null for non-existing element', async({page, server}) => { itFailsFirefox('should return null for non-existing element', async() => {
const { page } = getTestState();
await page.setContent('<html><body><div class="second"><div class="inner">B</div></div></body></html>'); await page.setContent('<html><body><div class="second"><div class="inner">B</div></div></body></html>');
const html = await page.$('html'); const html = await page.$('html');
const second = await html.$x(`/div[contains(@class, 'third')]`); const second = await html.$x(`/div[contains(@class, 'third')]`);
expect(second).toEqual([]); expect(second).toEqual([]);
}); });
}); });
}; });

View File

@ -17,14 +17,16 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, CHROME}) { describe('request interception', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describeFailsFirefox('Page.setRequestInterception', function() {
it('should intercept', async() => {
const { page, server } = getTestState();
describe_fails_ffox('Page.setRequestInterception', function() {
it('should intercept', async({page, server}) => {
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
if (utils.isFavicon(request)) { if (utils.isFavicon(request)) {
@ -45,7 +47,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
expect(response.remoteAddress().port).toBe(server.PORT); expect(response.remoteAddress().port).toBe(server.PORT);
}); });
it('should work when POST is redirected with 302', async({page, server}) => { it('should work when POST is redirected with 302', async() => {
const { page, server } = getTestState();
server.setRedirect('/rredirect', '/empty.html'); server.setRedirect('/rredirect', '/empty.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -61,7 +65,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]); ]);
}); });
// @see https://github.com/puppeteer/puppeteer/issues/3973 // @see https://github.com/puppeteer/puppeteer/issues/3973
it('should work when header manipulation headers with redirect', async({page, server}) => { it('should work when header manipulation headers with redirect', async() => {
const { page, server } = getTestState();
server.setRedirect('/rrredirect', '/empty.html'); server.setRedirect('/rrredirect', '/empty.html');
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
@ -73,7 +79,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await page.goto(server.PREFIX + '/rrredirect'); await page.goto(server.PREFIX + '/rrredirect');
}); });
// @see https://github.com/puppeteer/puppeteer/issues/4743 // @see https://github.com/puppeteer/puppeteer/issues/4743
it('should be able to remove headers', async({page, server}) => { it('should be able to remove headers', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
const headers = Object.assign({}, request.headers(), { const headers = Object.assign({}, request.headers(), {
@ -90,7 +98,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(serverRequest.headers.origin).toBe(undefined); expect(serverRequest.headers.origin).toBe(undefined);
}); });
it('should contain referer header', async({page, server}) => { it('should contain referer header', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
@ -102,7 +112,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests[1].url()).toContain('/one-style.css'); expect(requests[1].url()).toContain('/one-style.css');
expect(requests[1].headers().referer).toContain('/one-style.html'); expect(requests[1].headers().referer).toContain('/one-style.html');
}); });
it('should properly return navigation response when URL has cookies', async({page, server}) => { it('should properly return navigation response when URL has cookies', async() => {
const { page, server } = getTestState();
// Setup cookie. // Setup cookie.
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setCookie({ name: 'foo', value: 'bar'}); await page.setCookie({ name: 'foo', value: 'bar'});
@ -113,14 +125,18 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const response = await page.reload(); const response = await page.reload();
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
}); });
it('should stop intercepting', async({page, server}) => { it('should stop intercepting', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.once('request', request => request.continue()); page.once('request', request => request.continue());
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(false); await page.setRequestInterception(false);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
}); });
it('should show custom HTTP headers', async({page, server}) => { it('should show custom HTTP headers', async() => {
const { page, server } = getTestState();
await page.setExtraHTTPHeaders({ await page.setExtraHTTPHeaders({
foo: 'bar' foo: 'bar'
}); });
@ -133,7 +149,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
}); });
// @see https://github.com/puppeteer/puppeteer/issues/4337 // @see https://github.com/puppeteer/puppeteer/issues/4337
it('should work with redirect inside sync XHR', async({page, server}) => { it('should work with redirect inside sync XHR', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
server.setRedirect('/logo.png', '/pptr.png'); server.setRedirect('/logo.png', '/pptr.png');
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -146,7 +164,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
expect(status).toBe(200); expect(status).toBe(200);
}); });
it('should work with custom referer headers', async({page, server}) => { it('should work with custom referer headers', async() => {
const { page, server } = getTestState();
await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE }); await page.setExtraHTTPHeaders({ 'referer': server.EMPTY_PAGE });
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
@ -156,7 +176,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const response = await page.goto(server.EMPTY_PAGE); const response = await page.goto(server.EMPTY_PAGE);
expect(response.ok()).toBe(true); expect(response.ok()).toBe(true);
}); });
it('should be abortable', async({page, server}) => { it('should be abortable', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
if (request.url().endsWith('.css')) if (request.url().endsWith('.css'))
@ -171,7 +193,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.request().failure()).toBe(null); expect(response.request().failure()).toBe(null);
expect(failedRequests).toBe(1); expect(failedRequests).toBe(1);
}); });
it('should be abortable with custom error codes', async({page, server}) => { it('should be abortable with custom error codes', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
request.abort('internetdisconnected'); request.abort('internetdisconnected');
@ -182,7 +206,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(failedRequest).toBeTruthy(); expect(failedRequest).toBeTruthy();
expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED'); expect(failedRequest.failure().errorText).toBe('net::ERR_INTERNET_DISCONNECTED');
}); });
it('should send referer', async({page, server}) => { it('should send referer', async() => {
const { page, server } = getTestState();
await page.setExtraHTTPHeaders({ await page.setExtraHTTPHeaders({
referer: 'http://google.com/' referer: 'http://google.com/'
}); });
@ -194,18 +220,22 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]); ]);
expect(request.headers['referer']).toBe('http://google.com/'); expect(request.headers['referer']).toBe('http://google.com/');
}); });
it('should fail navigation when aborting main resource', async({page, server}) => { it('should fail navigation when aborting main resource', async() => {
const { page, server, isChrome } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => request.abort()); page.on('request', request => request.abort());
let error = null; let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e); await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
if (CHROME) if (isChrome)
expect(error.message).toContain('net::ERR_FAILED'); expect(error.message).toContain('net::ERR_FAILED');
else else
expect(error.message).toContain('NS_ERROR_FAILURE'); expect(error.message).toContain('NS_ERROR_FAILURE');
}); });
it('should work with redirects', async({page, server}) => { it('should work with redirects', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
@ -232,7 +262,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(request.redirectChain().indexOf(request)).toBe(i); expect(request.redirectChain().indexOf(request)).toBe(i);
} }
}); });
it('should work with redirects for subresources', async({page, server}) => { it('should work with redirects for subresources', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
@ -257,7 +289,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(redirectChain[0].url()).toContain('/one-style.css'); expect(redirectChain[0].url()).toContain('/one-style.css');
expect(redirectChain[2].url()).toContain('/three-style.css'); expect(redirectChain[2].url()).toContain('/three-style.css');
}); });
it('should be able to abort redirects', async({page, server}) => { it('should be able to abort redirects', async() => {
const { page, server, isChrome } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
server.setRedirect('/non-existing.json', '/non-existing-2.json'); server.setRedirect('/non-existing.json', '/non-existing-2.json');
server.setRedirect('/non-existing-2.json', '/simple.html'); server.setRedirect('/non-existing-2.json', '/simple.html');
@ -275,12 +309,14 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
return e.message; return e.message;
} }
}); });
if (CHROME) if (isChrome)
expect(result).toContain('Failed to fetch'); expect(result).toContain('Failed to fetch');
else else
expect(result).toContain('NetworkError'); expect(result).toContain('NetworkError');
}); });
it('should work with equal requests', async({page, server}) => { it('should work with equal requests', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
let responseCount = 1; let responseCount = 1;
server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + '')); server.setRoute('/zzz', (req, res) => res.end((responseCount++) * 11 + ''));
@ -303,7 +339,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
])); ]));
expect(results).toEqual(['11', 'FAILED', '22']); expect(results).toEqual(['11', 'FAILED', '22']);
}); });
it('should navigate to dataURL and fire dataURL requests', async({page, server}) => { it('should navigate to dataURL and fire dataURL requests', async() => {
const { page } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
@ -316,7 +354,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(dataURL); expect(requests[0].url()).toBe(dataURL);
}); });
it('should be able to fetch dataURL and fire dataURL requests', async({page, server}) => { it('should be able to fetch dataURL and fire dataURL requests', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
@ -330,7 +370,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(dataURL); expect(requests[0].url()).toBe(dataURL);
}); });
it('should navigate to URL with hash and and fire requests without hash', async({page, server}) => { it('should navigate to URL with hash and and fire requests without hash', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
const requests = []; const requests = [];
page.on('request', request => { page.on('request', request => {
@ -343,7 +385,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(1); expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE); expect(requests[0].url()).toBe(server.EMPTY_PAGE);
}); });
it('should work with encoded server', async({page, server}) => { it('should work with encoded server', async() => {
const { page, server } = getTestState();
// The requestWillBeSent will report encoded URL, whereas interception will // The requestWillBeSent will report encoded URL, whereas interception will
// report URL as-is. @see crbug.com/759388 // report URL as-is. @see crbug.com/759388
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -351,14 +395,18 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const response = await page.goto(server.PREFIX + '/some nonexisting page'); const response = await page.goto(server.PREFIX + '/some nonexisting page');
expect(response.status()).toBe(404); expect(response.status()).toBe(404);
}); });
it('should work with badly encoded server', async({page, server}) => { it('should work with badly encoded server', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
server.setRoute('/malformed?rnd=%911', (req, res) => res.end()); server.setRoute('/malformed?rnd=%911', (req, res) => res.end());
page.on('request', request => request.continue()); page.on('request', request => request.continue());
const response = await page.goto(server.PREFIX + '/malformed?rnd=%911'); const response = await page.goto(server.PREFIX + '/malformed?rnd=%911');
expect(response.status()).toBe(200); expect(response.status()).toBe(200);
}); });
it('should work with encoded server - 2', async({page, server}) => { it('should work with encoded server - 2', async() => {
const { page, server } = getTestState();
// The requestWillBeSent will report URL as-is, whereas interception will // The requestWillBeSent will report URL as-is, whereas interception will
// report encoded URL for stylesheet. @see crbug.com/759388 // report encoded URL for stylesheet. @see crbug.com/759388
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -372,7 +420,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(2); expect(requests.length).toBe(2);
expect(requests[1].response().status()).toBe(404); expect(requests[1].response().status()).toBe(404);
}); });
it('should not throw "Invalid Interception Id" if the request was cancelled', async({page, server}) => { it('should not throw "Invalid Interception Id" if the request was cancelled', async() => {
const { page, server } = getTestState();
await page.setContent('<iframe></iframe>'); await page.setContent('<iframe></iframe>');
await page.setRequestInterception(true); await page.setRequestInterception(true);
let request = null; let request = null;
@ -386,7 +436,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await request.continue().catch(e => error = e); await request.continue().catch(e => error = e);
expect(error).toBe(null); expect(error).toBe(null);
}); });
it('should throw if interception is not enabled', async({page, server}) => { it('should throw if interception is not enabled', async() => {
const { page, server } = getTestState();
let error = null; let error = null;
page.on('request', async request => { page.on('request', async request => {
try { try {
@ -398,7 +450,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(error.message).toContain('Request Interception is not enabled'); expect(error.message).toContain('Request Interception is not enabled');
}); });
it('should work with file URLs', async({page, server}) => { it('should work with file URLs', async() => {
const { page } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
const urls = new Set(); const urls = new Set();
page.on('request', request => { page.on('request', request => {
@ -412,13 +466,17 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Request.continue', function() { describeFailsFirefox('Request.continue', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => request.continue()); page.on('request', request => request.continue());
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
}); });
it('should amend HTTP headers', async({page, server}) => { it('should amend HTTP headers', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
const headers = Object.assign({}, request.headers()); const headers = Object.assign({}, request.headers());
@ -432,7 +490,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]); ]);
expect(request.headers['foo']).toBe('bar'); expect(request.headers['foo']).toBe('bar');
}); });
it('should redirect in a way non-observable to page', async({page, server}) => { it('should redirect in a way non-observable to page', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
const redirectURL = request.url().includes('/empty.html') ? server.PREFIX + '/consolelog.html' : undefined; const redirectURL = request.url().includes('/empty.html') ? server.PREFIX + '/consolelog.html' : undefined;
@ -444,7 +504,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(page.url()).toBe(server.EMPTY_PAGE); expect(page.url()).toBe(server.EMPTY_PAGE);
expect(consoleMessage.text()).toBe('yellow'); expect(consoleMessage.text()).toBe('yellow');
}); });
it('should amend method', async({page, server}) => { it('should amend method', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -457,7 +519,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]); ]);
expect(request.method).toBe('POST'); expect(request.method).toBe('POST');
}); });
it('should amend post data', async({page, server}) => { it('should amend post data', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true); await page.setRequestInterception(true);
@ -470,7 +534,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]); ]);
expect(await serverRequest.postBody).toBe('doggo'); expect(await serverRequest.postBody).toBe('doggo');
}); });
it('should amend both post data and method on navigation', async({page, server}) => { it('should amend both post data and method on navigation', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
request.continue({ method: 'POST', postData: 'doggo' }); request.continue({ method: 'POST', postData: 'doggo' });
@ -484,8 +550,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
}); });
}); });
describe_fails_ffox('Request.respond', function() { describeFailsFirefox('Request.respond', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
request.respond({ request.respond({
@ -501,7 +569,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.headers().foo).toBe('bar'); expect(response.headers().foo).toBe('bar');
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
}); });
it('should work with status code 422', async({page, server}) => { it('should work with status code 422', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
request.respond({ request.respond({
@ -514,7 +584,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.statusText()).toBe('Unprocessable Entity'); expect(response.statusText()).toBe('Unprocessable Entity');
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
}); });
it('should redirect', async({page, server}) => { it('should redirect', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
if (!request.url().includes('rrredirect')) { if (!request.url().includes('rrredirect')) {
@ -533,7 +605,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.request().redirectChain()[0].url()).toBe(server.PREFIX + '/rrredirect'); expect(response.request().redirectChain()[0].url()).toBe(server.PREFIX + '/rrredirect');
expect(response.url()).toBe(server.EMPTY_PAGE); expect(response.url()).toBe(server.EMPTY_PAGE);
}); });
it('should allow mocking binary responses', async({page, server}) => { it('should allow mocking binary responses', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
@ -551,7 +625,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const img = await page.$('img'); const img = await page.$('img');
expect(await img.screenshot()).toBeGolden('mock-binary-response.png'); expect(await img.screenshot()).toBeGolden('mock-binary-response.png');
}); });
it('should stringify intercepted request response headers', async({page, server}) => { it('should stringify intercepted request response headers', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true); await page.setRequestInterception(true);
page.on('request', request => { page.on('request', request => {
request.respond({ request.respond({
@ -569,8 +645,7 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
}); });
}); });
});
};
/** /**
* @param {string} path * @param {string} path

View File

@ -14,19 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, product}) { const expect = require('expect');
const {describe, xdescribe, fdescribe} = testRunner; const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; describe('Screenshots', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Page.screenshot', function() { describe('Page.screenshot', function() {
it_fails_ffox('should work', async({page, server}) => { itFailsFirefox('should work', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot(); const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-sanity.png'); expect(screenshot).toBeGolden('screenshot-sanity.png');
}); });
it_fails_ffox('should clip rect', async({page, server}) => { itFailsFirefox('should clip rect', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ const screenshot = await page.screenshot({
@ -39,7 +45,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
}); });
expect(screenshot).toBeGolden('screenshot-clip-rect.png'); expect(screenshot).toBeGolden('screenshot-clip-rect.png');
}); });
it_fails_ffox('should clip elements to the viewport', async({page, server}) => { itFailsFirefox('should clip elements to the viewport', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ const screenshot = await page.screenshot({
@ -52,7 +60,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
}); });
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png'); expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
}); });
it('should run in parallel', async({page, server}) => { it('should run in parallel', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const promises = []; const promises = [];
@ -69,7 +79,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshots = await Promise.all(promises); const screenshots = await Promise.all(promises);
expect(screenshots[1]).toBeGolden('grid-cell-1.png'); expect(screenshots[1]).toBeGolden('grid-cell-1.png');
}); });
it_fails_ffox('should take fullPage screenshots', async({page, server}) => { itFailsFirefox('should take fullPage screenshots', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ const screenshot = await page.screenshot({
@ -77,7 +89,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
}); });
expect(screenshot).toBeGolden('screenshot-grid-fullpage.png'); expect(screenshot).toBeGolden('screenshot-grid-fullpage.png');
}); });
it('should run in parallel in multiple pages', async({page, server, context}) => { it('should run in parallel in multiple pages', async() => {
const { server, context } = getTestState();
const N = 2; const N = 2;
const pages = await Promise.all(Array(N).fill(0).map(async() => { const pages = await Promise.all(Array(N).fill(0).map(async() => {
const page = await context.newPage(); const page = await context.newPage();
@ -92,19 +106,25 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`); expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`);
await Promise.all(pages.map(page => page.close())); await Promise.all(pages.map(page => page.close()));
}); });
it_fails_ffox('should allow transparency', async({page, server}) => { itFailsFirefox('should allow transparency', async() => {
const { page, server } = getTestState();
await page.setViewport({ width: 100, height: 100 }); await page.setViewport({ width: 100, height: 100 });
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true}); const screenshot = await page.screenshot({omitBackground: true});
expect(screenshot).toBeGolden('transparent.png'); expect(screenshot).toBeGolden('transparent.png');
}); });
it_fails_ffox('should render white background on jpeg file', async({page, server}) => { itFailsFirefox('should render white background on jpeg file', async() => {
const { page, server } = getTestState();
await page.setViewport({ width: 100, height: 100 }); await page.setViewport({ width: 100, height: 100 });
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'}); const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
expect(screenshot).toBeGolden('white.jpg'); expect(screenshot).toBeGolden('white.jpg');
}); });
it('should work with odd clip size on Retina displays', async({page, server}) => { it('should work with odd clip size on Retina displays', async() => {
const { page } = getTestState();
const screenshot = await page.screenshot({ const screenshot = await page.screenshot({
clip: { clip: {
x: 0, x: 0,
@ -115,7 +135,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
}); });
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png'); expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
}); });
it_fails_ffox('should return base64', async({page, server}) => { itFailsFirefox('should return base64', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ const screenshot = await page.screenshot({
@ -126,7 +148,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
}); });
describe('ElementHandle.screenshot', function() { describe('ElementHandle.screenshot', function() {
it('should work', async({page, server}) => { it('should work', async() => {
const { page, server } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(50, 100)); await page.evaluate(() => window.scrollBy(50, 100));
@ -134,7 +158,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png'); expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
}); });
it_fails_ffox('should take into account padding and border', async({page, server}) => { itFailsFirefox('should take into account padding and border', async() => {
const { page } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.setContent(` await page.setContent(`
something above something above
@ -151,7 +177,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-padding-border.png'); expect(screenshot).toBeGolden('screenshot-element-padding-border.png');
}); });
it_fails_ffox('should capture full element when larger than viewport', async({page, server}) => { itFailsFirefox('should capture full element when larger than viewport', async() => {
const { page } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.setContent(` await page.setContent(`
@ -175,7 +203,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 }); expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
}); });
it_fails_ffox('should scroll element into view', async({page, server}) => { itFailsFirefox('should scroll element into view', async() => {
const { page } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.setContent(` await page.setContent(`
something above something above
@ -198,7 +228,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png'); expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
}); });
it_fails_ffox('should work with a rotated element', async({page, server}) => { itFailsFirefox('should work with a rotated element', async() => {
const { page } = getTestState();
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.setContent(`<div style="position:absolute; await page.setContent(`<div style="position:absolute;
top: 100px; top: 100px;
@ -211,31 +243,38 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-rotate.png'); expect(screenshot).toBeGolden('screenshot-element-rotate.png');
}); });
it_fails_ffox('should fail to screenshot a detached element', async({page, server}) => { itFailsFirefox('should fail to screenshot a detached element', async() => {
const { page } = getTestState();
await page.setContent('<h1>remove this</h1>'); await page.setContent('<h1>remove this</h1>');
const elementHandle = await page.$('h1'); const elementHandle = await page.$('h1');
await page.evaluate(element => element.remove(), elementHandle); await page.evaluate(element => element.remove(), elementHandle);
const screenshotError = await elementHandle.screenshot().catch(error => error); const screenshotError = await elementHandle.screenshot().catch(error => error);
expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement'); expect(screenshotError.message).toBe('Node is either not visible or not an HTMLElement');
}); });
it_fails_ffox('should not hang with zero width/height element', async({page, server}) => { itFailsFirefox('should not hang with zero width/height element', async() => {
const { page } = getTestState();
await page.setContent('<div style="width: 50px; height: 0"></div>'); await page.setContent('<div style="width: 50px; height: 0"></div>');
const div = await page.$('div'); const div = await page.$('div');
const error = await div.screenshot().catch(e => e); const error = await div.screenshot().catch(e => e);
expect(error.message).toBe('Node has 0 height.'); expect(error.message).toBe('Node has 0 height.');
}); });
it_fails_ffox('should work for an element with fractional dimensions', async({page}) => { itFailsFirefox('should work for an element with fractional dimensions', async() => {
const { page } = getTestState();
await page.setContent('<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>'); await page.setContent('<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>');
const elementHandle = await page.$('div'); const elementHandle = await page.$('div');
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-fractional.png'); expect(screenshot).toBeGolden('screenshot-element-fractional.png');
}); });
it_fails_ffox('should work for an element with an offset', async({page}) => { itFailsFirefox('should work for an element with an offset', async() => {
const { page } = getTestState();
await page.setContent('<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>'); await page.setContent('<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>');
const elementHandle = await page.$('div'); const elementHandle = await page.$('div');
const screenshot = await elementHandle.screenshot(); const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png'); expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png');
}); });
}); });
});
};

View File

@ -16,151 +16,176 @@
const utils = require('./utils'); const utils = require('./utils');
const {waitEvent} = utils; const {waitEvent} = utils;
const expect = require('expect');
const {getTestState, setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, puppeteer}) { describe('Target', function() {
const {describe, xdescribe, fdescribe} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Target', function() { it('Browser.targets should return all of the targets', async() => {
it('Browser.targets should return all of the targets', async({page, server, browser}) => { const { browser } = getTestState();
// The pages will be the testing page and the original newtab page
const targets = browser.targets(); // The pages will be the testing page and the original newtab page
expect(targets.some(target => target.type() === 'page' && const targets = browser.targets();
expect(targets.some(target => target.type() === 'page' &&
target.url() === 'about:blank')).toBeTruthy(); target.url() === 'about:blank')).toBeTruthy();
expect(targets.some(target => target.type() === 'browser')).toBeTruthy(); expect(targets.some(target => target.type() === 'browser')).toBeTruthy();
}); });
it('Browser.pages should return all of the pages', async({page, server, context}) => { it('Browser.pages should return all of the pages', async() => {
// The pages will be the testing page const { page, context } = getTestState();
const allPages = await context.pages();
expect(allPages.length).toBe(1);
expect(allPages).toContain(page);
expect(allPages[0]).not.toBe(allPages[1]);
});
it('should contain browser target', async({browser}) => {
const targets = browser.targets();
const browserTarget = targets.find(target => target.type() === 'browser');
expect(browserTarget).toBeTruthy();
});
it('should be able to use the default page in the browser', async({page, server, browser}) => {
// The pages will be the testing page and the original newtab page
const allPages = await browser.pages();
const originalPage = allPages.find(p => p !== page);
expect(await originalPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
expect(await originalPage.$('body')).toBeTruthy();
});
it_fails_ffox('should report when a new page is created and closed', async({page, server, context}) => {
const [otherPage] = await Promise.all([
context.waitForTarget(target => target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html').then(target => target.page()),
page.evaluate(url => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html'),
]);
expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX);
expect(await otherPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
expect(await otherPage.$('body')).toBeTruthy();
let allPages = await context.pages(); // The pages will be the testing page
expect(allPages).toContain(page); const allPages = await context.pages();
expect(allPages).toContain(otherPage); expect(allPages.length).toBe(1);
expect(allPages).toContain(page);
expect(allPages[0]).not.toBe(allPages[1]);
});
it('should contain browser target', async() => {
const { browser } = getTestState();
const closePagePromise = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target.page()))); const targets = browser.targets();
await otherPage.close(); const browserTarget = targets.find(target => target.type() === 'browser');
expect(await closePagePromise).toBe(otherPage); expect(browserTarget).toBeTruthy();
});
it('should be able to use the default page in the browser', async() => {
const { page, browser } = getTestState();
allPages = await Promise.all(context.targets().map(target => target.page())); // The pages will be the testing page and the original newtab page
expect(allPages).toContain(page); const allPages = await browser.pages();
expect(allPages).not.toContain(otherPage); const originalPage = allPages.find(p => p !== page);
}); expect(await originalPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
it_fails_ffox('should report when a service worker is created and destroyed', async({page, server, context}) => { expect(await originalPage.$('body')).toBeTruthy();
await page.goto(server.EMPTY_PAGE); });
const createdTarget = new Promise(fulfill => context.once('targetcreated', target => fulfill(target))); itFailsFirefox('should report when a new page is created and closed', async() => {
const { page, server, context } = getTestState();
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); const [otherPage] = await Promise.all([
context.waitForTarget(target => target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html').then(target => target.page()),
page.evaluate(url => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html'),
]);
expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX);
expect(await otherPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
expect(await otherPage.$('body')).toBeTruthy();
expect((await createdTarget).type()).toBe('service_worker'); let allPages = await context.pages();
expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js'); expect(allPages).toContain(page);
expect(allPages).toContain(otherPage);
const destroyedTarget = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target))); const closePagePromise = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target.page())));
await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister())); await otherPage.close();
expect(await destroyedTarget).toBe(await createdTarget); expect(await closePagePromise).toBe(otherPage);
});
it_fails_ffox('should create a worker from a service worker', async({page, server, context}) => {
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
const target = await context.waitForTarget(target => target.type() === 'service_worker'); allPages = await Promise.all(context.targets().map(target => target.page()));
const worker = await target.worker(); expect(allPages).toContain(page);
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); expect(allPages).not.toContain(otherPage);
}); });
it_fails_ffox('should create a worker from a shared worker', async({page, server, context}) => { itFailsFirefox('should report when a service worker is created and destroyed', async() => {
await page.goto(server.EMPTY_PAGE); const { page, server, context } = getTestState();
await page.evaluate(() => {
new SharedWorker('data:text/javascript,console.log("hi")');
});
const target = await context.waitForTarget(target => target.type() === 'shared_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object SharedWorkerGlobalScope]');
});
it_fails_ffox('should report when a target url changes', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
let changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target)));
await page.goto(server.CROSS_PROCESS_PREFIX + '/');
expect((await changedTarget).url()).toBe(server.CROSS_PROCESS_PREFIX + '/');
changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target))); await page.goto(server.EMPTY_PAGE);
await page.goto(server.EMPTY_PAGE); const createdTarget = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
expect((await changedTarget).url()).toBe(server.EMPTY_PAGE);
});
it_fails_ffox('should not report uninitialized pages', async({page, server, context}) => {
let targetChanged = false;
const listener = () => targetChanged = true;
context.on('targetchanged', listener);
const targetPromise = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
const newPagePromise = context.newPage();
const target = await targetPromise;
expect(target.url()).toBe('about:blank');
const newPage = await newPagePromise; await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
const targetPromise2 = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
const evaluatePromise = newPage.evaluate(() => window.open('about:blank')); expect((await createdTarget).type()).toBe('service_worker');
const target2 = await targetPromise2; expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js');
expect(target2.url()).toBe('about:blank');
await evaluatePromise; const destroyedTarget = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target)));
await newPage.close(); await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister()));
expect(targetChanged).toBe(false, 'target should not be reported as changed'); expect(await destroyedTarget).toBe(await createdTarget);
context.removeListener('targetchanged', listener); });
}); itFailsFirefox('should create a worker from a service worker', async() => {
it_fails_ffox('should not crash while redirecting if original request was missed', async({page, server, context}) => { const { page, server, context } = getTestState();
let serverResponse = null;
server.setRoute('/one-style.css', (req, res) => serverResponse = res); await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
// Open a new page. Use window.open to connect to the page later.
await Promise.all([ const target = await context.waitForTarget(target => target.type() === 'service_worker');
page.evaluate(url => window.open(url), server.PREFIX + '/one-style.html'), const worker = await target.worker();
server.waitForRequest('/one-style.css') expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
]); });
// Connect to the opened page. itFailsFirefox('should create a worker from a shared worker', async() => {
const target = await context.waitForTarget(target => target.url().includes('one-style.html')); const { page, server, context } = getTestState();
const newPage = await target.page();
// Issue a redirect. await page.goto(server.EMPTY_PAGE);
serverResponse.writeHead(302, { location: '/injectedstyle.css' }); await page.evaluate(() => {
serverResponse.end(); new SharedWorker('data:text/javascript,console.log("hi")');
// Wait for the new page to load.
await waitEvent(newPage, 'load');
// Cleanup.
await newPage.close();
});
it_fails_ffox('should have an opener', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
const [createdTarget] = await Promise.all([
new Promise(fulfill => context.once('targetcreated', target => fulfill(target))),
page.goto(server.PREFIX + '/popup/window-open.html')
]);
expect((await createdTarget.page()).url()).toBe(server.PREFIX + '/popup/popup.html');
expect(createdTarget.opener()).toBe(page.target());
expect(page.target().opener()).toBe(null);
}); });
const target = await context.waitForTarget(target => target.type() === 'shared_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object SharedWorkerGlobalScope]');
});
itFailsFirefox('should report when a target url changes', async() => {
const { page, server, context } = getTestState();
await page.goto(server.EMPTY_PAGE);
let changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target)));
await page.goto(server.CROSS_PROCESS_PREFIX + '/');
expect((await changedTarget).url()).toBe(server.CROSS_PROCESS_PREFIX + '/');
changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target)));
await page.goto(server.EMPTY_PAGE);
expect((await changedTarget).url()).toBe(server.EMPTY_PAGE);
});
itFailsFirefox('should not report uninitialized pages', async() => {
const { context } = getTestState();
let targetChanged = false;
const listener = () => targetChanged = true;
context.on('targetchanged', listener);
const targetPromise = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
const newPagePromise = context.newPage();
const target = await targetPromise;
expect(target.url()).toBe('about:blank');
const newPage = await newPagePromise;
const targetPromise2 = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
const evaluatePromise = newPage.evaluate(() => window.open('about:blank'));
const target2 = await targetPromise2;
expect(target2.url()).toBe('about:blank');
await evaluatePromise;
await newPage.close();
expect(targetChanged).toBe(false, 'target should not be reported as changed');
context.removeListener('targetchanged', listener);
});
itFailsFirefox('should not crash while redirecting if original request was missed', async() => {
const { page, server, context } = getTestState();
let serverResponse = null;
server.setRoute('/one-style.css', (req, res) => serverResponse = res);
// Open a new page. Use window.open to connect to the page later.
await Promise.all([
page.evaluate(url => window.open(url), server.PREFIX + '/one-style.html'),
server.waitForRequest('/one-style.css')
]);
// Connect to the opened page.
const target = await context.waitForTarget(target => target.url().includes('one-style.html'));
const newPage = await target.page();
// Issue a redirect.
serverResponse.writeHead(302, { location: '/injectedstyle.css' });
serverResponse.end();
// Wait for the new page to load.
await waitEvent(newPage, 'load');
// Cleanup.
await newPage.close();
});
itFailsFirefox('should have an opener', async() => {
const { page, server, context } = getTestState();
await page.goto(server.EMPTY_PAGE);
const [createdTarget] = await Promise.all([
new Promise(fulfill => context.once('targetcreated', target => fulfill(target))),
page.goto(server.PREFIX + '/popup/window-open.html')
]);
expect((await createdTarget.page()).url()).toBe(server.PREFIX + '/popup/popup.html');
expect(createdTarget.opener()).toBe(page.target());
expect(page.target().opener()).toBe(null);
}); });
describe('Browser.waitForTarget', () => { describe('Browser.waitForTarget', () => {
it_fails_ffox('should wait for a target', async function({browser, server}) { itFailsFirefox('should wait for a target', async() => {
const { browser, server } = getTestState();
let resolved = false; let resolved = false;
const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE); const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE);
targetPromise.then(() => resolved = true); targetPromise.then(() => resolved = true);
@ -171,7 +196,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await target.page()).toBe(page); expect(await target.page()).toBe(page);
await page.close(); await page.close();
}); });
it('should timeout waiting for a non-existent target', async function({browser, server}) { it('should timeout waiting for a non-existent target', async() => {
const { browser, server, puppeteer } = getTestState();
let error = null; let error = null;
await browser.waitForTarget(target => target.url() === server.EMPTY_PAGE, { await browser.waitForTarget(target => target.url() === server.EMPTY_PAGE, {
timeout: 1 timeout: 1
@ -179,4 +206,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
}); });
}); });
}; });

View File

@ -1,121 +0,0 @@
/**
* Copyright 2017 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 path = require('path');
const {TestServer} = require('../utils/testserver/');
const {TestRunner, Reporter} = require('../utils/testrunner/');
const utils = require('./utils');
let parallel = 1;
if (process.env.PPTR_PARALLEL_TESTS)
parallel = parseInt(process.env.PPTR_PARALLEL_TESTS.trim(), 10);
const parallelArgIndex = process.argv.indexOf('-j');
if (parallelArgIndex !== -1)
parallel = parseInt(process.argv[parallelArgIndex + 1], 10);
require('events').defaultMaxListeners *= parallel;
const defaultTimeout = process.env.PUPPETEER_PRODUCT === 'firefox' ? 15 * 1000 : 10 * 1000;
let timeout = process.env.APPVEYOR ? 20 * 1000 : defaultTimeout;
if (!isNaN(process.env.TIMEOUT))
timeout = parseInt(process.env.TIMEOUT, defaultTimeout);
const testRunner = new TestRunner({
timeout,
parallel,
breakOnFailure: process.argv.indexOf('--break-on-failure') !== -1,
});
const {describe, fdescribe, beforeAll, afterAll, beforeEach, afterEach} = testRunner;
console.log('Testing on Node', process.version);
beforeAll(async state => {
const assetsPath = path.join(__dirname, 'assets');
const cachedPath = path.join(__dirname, 'assets', 'cached');
const port = 8907 + state.parallelIndex * 2;
state.server = await TestServer.create(assetsPath, port);
state.server.enableHTTPCache(cachedPath);
state.server.PORT = port;
state.server.PREFIX = `http://localhost:${port}`;
state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`;
state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
const httpsPort = port + 1;
state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
state.httpsServer.enableHTTPCache(cachedPath);
state.httpsServer.PORT = httpsPort;
state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
});
afterAll(async({server, httpsServer}) => {
await Promise.all([
server.stop(),
httpsServer.stop(),
]);
});
beforeEach(async({server, httpsServer}) => {
server.reset();
httpsServer.reset();
});
const CHROMIUM_NO_COVERAGE = new Set([
'page.emulateMedia', // Legacy alias for `page.emulateMediaType`.
]);
switch (process.env.PUPPETEER_PRODUCT) {
case 'firefox':
testRunner.addTestDSL('it_fails_ffox', 'skip');
testRunner.addSuiteDSL('describe_fails_ffox', 'skip');
describe('Firefox', () => {
require('./puppeteer.spec.js').addTests({
product: 'Firefox',
puppeteerPath: utils.projectRoot(),
testRunner,
});
});
break;
case 'chrome':
default:
testRunner.addTestDSL('it_fails_ffox', 'run');
testRunner.addSuiteDSL('describe_fails_ffox', 'run');
describe('Chromium', () => {
require('./puppeteer.spec.js').addTests({
product: 'Chromium',
puppeteerPath: utils.projectRoot(),
testRunner,
});
if (process.env.COVERAGE)
utils.recordAPICoverage(testRunner, require('../lib/api'), require('../lib/Events').Events, CHROMIUM_NO_COVERAGE);
});
}
if (process.env.CI && testRunner.hasFocusedTestsOrSuites()) {
console.error('ERROR: "focused" tests/suites are prohibitted on bots. Remove any "fit"/"fdescribe" declarations.');
process.exit(1);
}
new Reporter(testRunner, {
verbose: process.argv.includes('--verbose'),
summary: !process.argv.includes('--verbose'),
projectFolder: utils.projectRoot(),
showSlowTests: process.env.CI ? 5 : 0,
});
(async() => {
testRunner.run();
})();

View File

@ -14,24 +14,28 @@
* limitations under the License. * limitations under the License.
*/ */
module.exports.addTests = function({testRunner, expect, puppeteer}) { const expect = require('expect');
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
const {it, fit, xit, it_fails_ffox} = testRunner;
const iPhone = puppeteer.devices['iPhone 6']; describeFailsFirefox('Touchscreen', function() {
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; setupTestBrowserHooks();
describe_fails_ffox('Touchscreen', function() { setupTestPageAndContextHooks();
it('should tap the button', async({page, server}) => {
await page.emulate(iPhone); it('should tap the button', async() => {
await page.goto(server.PREFIX + '/input/button.html'); const {puppeteer, page, server} = getTestState();
await page.tap('button'); const iPhone = puppeteer.devices['iPhone 6'];
expect(await page.evaluate(() => result)).toBe('Clicked'); await page.emulate(iPhone);
}); await page.goto(server.PREFIX + '/input/button.html');
it('should report touches', async({page, server}) => { await page.tap('button');
await page.emulate(iPhone); expect(await page.evaluate(() => result)).toBe('Clicked');
await page.goto(server.PREFIX + '/input/touches.html');
const button = await page.$('button');
await button.tap();
expect(await page.evaluate(() => getResult())).toEqual(['Touchstart: 0', 'Touchend: 0']);
});
}); });
}; it('should report touches', async() => {
const {puppeteer, page, server} = getTestState();
const iPhone = puppeteer.devices['iPhone 6'];
await page.emulate(iPhone);
await page.goto(server.PREFIX + '/input/touches.html');
const button = await page.$('button');
await button.tap();
expect(await page.evaluate(() => getResult())).toEqual(['Touchstart: 0', 'Touchend: 0']);
});
});

View File

@ -16,78 +16,97 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) { describeChromeOnly('Tracing', function() {
const {describe, xdescribe, fdescribe} = testRunner; let outputFile;
const {it, fit, xit} = testRunner; let browser;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; let page;
describe('Tracing', function() { /* we manually manage the browser here as we want a new browser for each
beforeEach(async function(state) { * individual test, which isn't the default behaviour of getTestState()
state.outputFile = path.join(__dirname, 'assets', `trace-${state.parallelIndex}.json`); */
state.browser = await puppeteer.launch(defaultBrowserOptions);
state.page = await state.browser.newPage();
});
afterEach(async function(state) {
await state.browser.close();
state.browser = null;
state.page = null;
if (fs.existsSync(state.outputFile)) {
fs.unlinkSync(state.outputFile);
state.outputFile = null;
}
});
it('should output a trace', async({page, server, outputFile}) => {
await page.tracing.start({screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
await page.tracing.stop();
expect(fs.existsSync(outputFile)).toBe(true);
});
it('should run with custom categories if provided', async({page, outputFile}) => {
await page.tracing.start({path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
await page.tracing.stop();
const traceJson = JSON.parse(fs.readFileSync(outputFile)); beforeEach(async() => {
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); const {defaultBrowserOptions, puppeteer} = getTestState();
}); browser = await puppeteer.launch(defaultBrowserOptions);
it('should throw if tracing on two pages', async({page, server, browser, outputFile}) => { page = await browser.newPage();
await page.tracing.start({path: outputFile}); outputFile = path.join(__dirname, 'assets', 'trace.json');
const newPage = await browser.newPage();
let error = null;
await newPage.tracing.start({path: outputFile}).catch(e => error = e);
await newPage.close();
expect(error).toBeTruthy();
await page.tracing.stop();
});
it('should return a buffer', async({page, server, outputFile}) => {
await page.tracing.start({screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
const trace = await page.tracing.stop();
const buf = fs.readFileSync(outputFile);
expect(trace.toString()).toEqual(buf.toString());
});
it('should work without options', async({page, server, outputFile}) => {
await page.tracing.start();
await page.goto(server.PREFIX + '/grid.html');
const trace = await page.tracing.stop();
expect(trace).toBeTruthy();
});
it('should return null in case of Buffer error', async({page, server}) => {
await page.tracing.start({screenshots: true});
await page.goto(server.PREFIX + '/grid.html');
const oldBufferConcat = Buffer.concat;
Buffer.concat = bufs => {
throw 'error';
};
const trace = await page.tracing.stop();
expect(trace).toEqual(null);
Buffer.concat = oldBufferConcat;
});
it('should support a buffer without a path', async({page, server}) => {
await page.tracing.start({screenshots: true});
await page.goto(server.PREFIX + '/grid.html');
const trace = await page.tracing.stop();
expect(trace.toString()).toContain('screenshot');
});
}); });
};
afterEach(async() => {
await browser.close();
browser = null;
page = null;
if (fs.existsSync(outputFile)) {
fs.unlinkSync(outputFile);
outputFile = null;
}
});
it('should output a trace', async() => {
const { server} = getTestState();
await page.tracing.start({screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
await page.tracing.stop();
expect(fs.existsSync(outputFile)).toBe(true);
});
it('should run with custom categories if provided', async() => {
await page.tracing.start({path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
await page.tracing.stop();
const traceJson = JSON.parse(fs.readFileSync(outputFile));
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires');
});
it('should throw if tracing on two pages', async() => {
await page.tracing.start({path: outputFile});
const newPage = await browser.newPage();
let error = null;
await newPage.tracing.start({path: outputFile}).catch(e => error = e);
await newPage.close();
expect(error).toBeTruthy();
await page.tracing.stop();
});
it('should return a buffer', async() => {
const { server } = getTestState();
await page.tracing.start({screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
const trace = await page.tracing.stop();
const buf = fs.readFileSync(outputFile);
expect(trace.toString()).toEqual(buf.toString());
});
it('should work without options', async() => {
const { server } = getTestState();
await page.tracing.start();
await page.goto(server.PREFIX + '/grid.html');
const trace = await page.tracing.stop();
expect(trace).toBeTruthy();
});
it('should return null in case of Buffer error', async() => {
const { server } = getTestState();
await page.tracing.start({screenshots: true});
await page.goto(server.PREFIX + '/grid.html');
const oldBufferConcat = Buffer.concat;
Buffer.concat = bufs => {
throw 'error';
};
const trace = await page.tracing.stop();
expect(trace).toEqual(null);
Buffer.concat = oldBufferConcat;
});
it('should support a buffer without a path', async() => {
const { server } = getTestState();
await page.tracing.start({screenshots: true});
await page.goto(server.PREFIX + '/grid.html');
const trace = await page.tracing.stop();
expect(trace.toString()).toContain('screenshot');
});
});

View File

@ -20,59 +20,7 @@ const expect = require('expect');
const GoldenUtils = require('./golden-utils'); const GoldenUtils = require('./golden-utils');
const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..'); const PROJECT_ROOT = fs.existsSync(path.join(__dirname, '..', 'package.json')) ? path.join(__dirname, '..') : path.join(__dirname, '..', '..');
const COVERAGE_TESTSUITE_NAME = '**API COVERAGE**';
/**
* @param {Map<string, boolean>} 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);
});
}
}
const utils = module.exports = { const utils = module.exports = {
recordAPICoverage: function(testRunner, api, events, disabled) {
const coverage = new Map();
for (const [className, classType] of Object.entries(api))
traceAPICoverage(coverage, events, className, classType);
testRunner.describe(COVERAGE_TESTSUITE_NAME, () => {
testRunner.it('should call all API methods', () => {
const missingMethods = [];
for (const method of coverage.keys()) {
if (!coverage.get(method) && !disabled.has(method))
missingMethods.push(method);
}
if (missingMethods.length)
throw new Error('Certain API Methods are not called: ' + missingMethods.join(', '));
});
});
},
extendExpectWithToBeGolden: function(goldenDir, outputDir) { extendExpectWithToBeGolden: function(goldenDir, outputDir) {
expect.extend({ expect.extend({
toBeGolden: (testScreenshot, goldenFilePath) => { toBeGolden: (testScreenshot, goldenFilePath) => {

View File

@ -15,14 +15,17 @@
*/ */
const utils = require('./utils'); const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, product, puppeteer}) { describe('waittask specs', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit, it_fails_ffox} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.waitFor', function() { describe('Page.waitFor', function() {
it_fails_ffox('should wait for selector', async({page, server}) => { itFailsFirefox('should wait for selector', async() => {
const { page, server } = getTestState();
let found = false; let found = false;
const waitFor = page.waitFor('div').then(() => found = true); const waitFor = page.waitFor('div').then(() => found = true);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -31,7 +34,10 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitFor; await waitFor;
expect(found).toBe(true); expect(found).toBe(true);
}); });
it_fails_ffox('should wait for an xpath', async({page, server}) => {
itFailsFirefox('should wait for an xpath', async() => {
const { page, server } = getTestState();
let found = false; let found = false;
const waitFor = page.waitFor('//div').then(() => found = true); const waitFor = page.waitFor('//div').then(() => found = true);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -40,47 +46,63 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitFor; await waitFor;
expect(found).toBe(true); expect(found).toBe(true);
}); });
it_fails_ffox('should not allow you to select an element with single slash xpath', async({page, server}) => { itFailsFirefox('should not allow you to select an element with single slash xpath', async() => {
const { page } = getTestState();
await page.setContent(`<div>some text</div>`); await page.setContent(`<div>some text</div>`);
let error = null; let error = null;
await page.waitFor('/html/body/div').catch(e => error = e); await page.waitFor('/html/body/div').catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
}); });
it('should timeout', async({page, server}) => { it('should timeout', async() => {
const { page } = getTestState();
const startTime = Date.now(); const startTime = Date.now();
const timeout = 42; const timeout = 42;
await page.waitFor(timeout); await page.waitFor(timeout);
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2); expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
}); });
it('should work with multiline body', async({page, server}) => { it('should work with multiline body', async() => {
const { page } = getTestState();
const result = await page.waitForFunction(` const result = await page.waitForFunction(`
(() => true)() (() => true)()
`); `);
expect(await result.jsonValue()).toBe(true); expect(await result.jsonValue()).toBe(true);
}); });
it('should wait for predicate', async({page, server}) => { it('should wait for predicate', async() => {
const { page } = getTestState();
await Promise.all([ await Promise.all([
page.waitFor(() => window.innerWidth < 100), page.waitFor(() => window.innerWidth < 100),
page.setViewport({width: 10, height: 10}), page.setViewport({width: 10, height: 10}),
]); ]);
}); });
it('should throw when unknown type', async({page, server}) => { it('should throw when unknown type', async() => {
const { page } = getTestState();
let error = null; let error = null;
await page.waitFor({foo: 'bar'}).catch(e => error = e); await page.waitFor({foo: 'bar'}).catch(e => error = e);
expect(error.message).toContain('Unsupported target type'); expect(error.message).toContain('Unsupported target type');
}); });
it('should wait for predicate with arguments', async({page, server}) => { it('should wait for predicate with arguments', async() => {
const { page } = getTestState();
await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2); await page.waitFor((arg1, arg2) => arg1 !== arg2, {}, 1, 2);
}); });
}); });
describe('Frame.waitForFunction', function() { describe('Frame.waitForFunction', function() {
it('should accept a string', async({page, server}) => { it('should accept a string', async() => {
const { page } = getTestState();
const watchdog = page.waitForFunction('window.__FOO === 1'); const watchdog = page.waitForFunction('window.__FOO === 1');
await page.evaluate(() => window.__FOO = 1); await page.evaluate(() => window.__FOO = 1);
await watchdog; await watchdog;
}); });
it_fails_ffox('should work when resolved right before execution context disposal', async({page, server}) => { itFailsFirefox('should work when resolved right before execution context disposal', async() => {
const { page } = getTestState();
await page.evaluateOnNewDocument(() => window.__RELOADED = true); await page.evaluateOnNewDocument(() => window.__RELOADED = true);
await page.waitForFunction(() => { await page.waitForFunction(() => {
if (!window.__RELOADED) if (!window.__RELOADED)
@ -88,7 +110,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
return true; return true;
}); });
}); });
it('should poll on interval', async({page, server}) => { it('should poll on interval', async() => {
const { page } = getTestState();
let success = false; let success = false;
const startTime = Date.now(); const startTime = Date.now();
const polling = 100; const polling = 100;
@ -100,7 +124,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await watchdog; await watchdog;
expect(Date.now() - startTime).not.toBeLessThan(polling / 2); expect(Date.now() - startTime).not.toBeLessThan(polling / 2);
}); });
it('should poll on mutation', async({page, server}) => { it('should poll on mutation', async() => {
const { page } = getTestState();
let success = false; let success = false;
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'}) const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'})
.then(() => success = true); .then(() => success = true);
@ -109,12 +135,16 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await page.evaluate(() => document.body.appendChild(document.createElement('div'))); await page.evaluate(() => document.body.appendChild(document.createElement('div')));
await watchdog; await watchdog;
}); });
it('should poll on raf', async({page, server}) => { it('should poll on raf', async() => {
const { page } = getTestState();
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'}); const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'});
await page.evaluate(() => window.__FOO = 'hit'); await page.evaluate(() => window.__FOO = 'hit');
await watchdog; await watchdog;
}); });
it_fails_ffox('should work with strict CSP policy', async({page, server}) => { itFailsFirefox('should work with strict CSP policy', async() => {
const { page, server } = getTestState();
server.setCSP('/empty.html', 'script-src ' + server.PREFIX); server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
let error = null; let error = null;
@ -124,7 +154,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
]); ]);
expect(error).toBe(null); expect(error).toBe(null);
}); });
it('should throw on bad polling value', async({page, server}) => { it('should throw on bad polling value', async() => {
const { page } = getTestState();
let error = null; let error = null;
try { try {
await page.waitForFunction(() => !!document.body, {polling: 'unknown'}); await page.waitForFunction(() => !!document.body, {polling: 'unknown'});
@ -134,7 +166,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('polling'); expect(error.message).toContain('polling');
}); });
it('should throw negative polling interval', async({page, server}) => { it('should throw negative polling interval', async() => {
const { page } = getTestState();
let error = null; let error = null;
try { try {
await page.waitForFunction(() => !!document.body, {polling: -10}); await page.waitForFunction(() => !!document.body, {polling: -10});
@ -144,13 +178,19 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('Cannot poll with non-positive interval'); expect(error.message).toContain('Cannot poll with non-positive interval');
}); });
it('should return the success value as a JSHandle', async({page}) => { it('should return the success value as a JSHandle', async() => {
const { page } = getTestState();
expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5); expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
}); });
it('should return the window as a success value', async({ page }) => { it('should return the window as a success value', async() => {
const { page } = getTestState();
expect(await page.waitForFunction(() => window)).toBeTruthy(); expect(await page.waitForFunction(() => window)).toBeTruthy();
}); });
it_fails_ffox('should accept ElementHandle arguments', async({page}) => { itFailsFirefox('should accept ElementHandle arguments', async() => {
const { page } = getTestState();
await page.setContent('<div></div>'); await page.setContent('<div></div>');
const div = await page.$('div'); const div = await page.$('div');
let resolved = false; let resolved = false;
@ -159,21 +199,27 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await page.evaluate(element => element.remove(), div); await page.evaluate(element => element.remove(), div);
await waitForFunction; await waitForFunction;
}); });
it('should respect timeout', async({page}) => { it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null; let error = null;
await page.waitForFunction('false', {timeout: 10}).catch(e => error = e); await page.waitForFunction('false', {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('waiting for function failed: timeout'); expect(error.message).toContain('waiting for function failed: timeout');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
}); });
it('should respect default timeout', async({page}) => { it('should respect default timeout', async() => {
const { page, puppeteer } = getTestState();
page.setDefaultTimeout(1); page.setDefaultTimeout(1);
let error = null; let error = null;
await page.waitForFunction('false').catch(e => error = e); await page.waitForFunction('false').catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
expect(error.message).toContain('waiting for function failed: timeout'); expect(error.message).toContain('waiting for function failed: timeout');
}); });
it('should disable timeout when its set to 0', async({page}) => { it('should disable timeout when its set to 0', async() => {
const { page } = getTestState();
const watchdog = page.waitForFunction(() => { const watchdog = page.waitForFunction(() => {
window.__counter = (window.__counter || 0) + 1; window.__counter = (window.__counter || 0) + 1;
return window.__injected; return window.__injected;
@ -182,7 +228,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await page.evaluate(() => window.__injected = true); await page.evaluate(() => window.__injected = true);
await watchdog; await watchdog;
}); });
it('should survive cross-process navigation', async({page, server}) => { it('should survive cross-process navigation', async() => {
const { page, server } = getTestState();
let fooFound = false; let fooFound = false;
const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true); const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -195,7 +243,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitForFunction; await waitForFunction;
expect(fooFound).toBe(true); expect(fooFound).toBe(true);
}); });
it('should survive navigations', async({page, server}) => { it('should survive navigations', async() => {
const { page, server } = getTestState();
const watchdog = page.waitForFunction(() => window.__done); const watchdog = page.waitForFunction(() => window.__done);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.goto(server.PREFIX + '/consolelog.html'); await page.goto(server.PREFIX + '/consolelog.html');
@ -204,10 +254,12 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
}); });
}); });
describe_fails_ffox('Frame.waitForSelector', function() { describeFailsFirefox('Frame.waitForSelector', function() {
const addElement = tag => document.body.appendChild(document.createElement(tag)); const addElement = tag => document.body.appendChild(document.createElement(tag));
it('should immediately resolve promise if node exists', async({page, server}) => { it('should immediately resolve promise if node exists', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame(); const frame = page.mainFrame();
await frame.waitForSelector('*'); await frame.waitForSelector('*');
@ -215,7 +267,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await frame.waitForSelector('div'); await frame.waitForSelector('div');
}); });
it('should work with removed MutationObserver', async({page, server}) => { it('should work with removed MutationObserver', async() => {
const { page } = getTestState();
await page.evaluate(() => delete window.MutationObserver); await page.evaluate(() => delete window.MutationObserver);
const [handle] = await Promise.all([ const [handle] = await Promise.all([
page.waitForSelector('.zombo'), page.waitForSelector('.zombo'),
@ -224,7 +278,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything'); expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
}); });
it('should resolve promise when node is added', async({page, server}) => { it('should resolve promise when node is added', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame(); const frame = page.mainFrame();
const watchdog = frame.waitForSelector('div'); const watchdog = frame.waitForSelector('div');
@ -235,7 +291,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(tagName).toBe('DIV'); expect(tagName).toBe('DIV');
}); });
it('should work when node is added through innerHTML', async({page, server}) => { it('should work when node is added through innerHTML', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const watchdog = page.waitForSelector('h3 div'); const watchdog = page.waitForSelector('h3 div');
await page.evaluate(addElement, 'span'); await page.evaluate(addElement, 'span');
@ -243,7 +301,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await watchdog; await watchdog;
}); });
it('Page.waitForSelector is shortcut for main frame', async({page, server}) => { it('Page.waitForSelector is shortcut for main frame', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const otherFrame = page.frames()[1]; const otherFrame = page.frames()[1];
@ -254,7 +314,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(eHandle.executionContext().frame()).toBe(page.mainFrame()); expect(eHandle.executionContext().frame()).toBe(page.mainFrame());
}); });
it('should run in specified frame', async({page, server}) => { it('should run in specified frame', async() => {
const { page, server } = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1]; const frame1 = page.frames()[1];
@ -266,7 +328,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(eHandle.executionContext().frame()).toBe(frame2); expect(eHandle.executionContext().frame()).toBe(frame2);
}); });
it('should throw when frame is detached', async({page, server}) => { it('should throw when frame is detached', async() => {
const { page, server } = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1]; const frame = page.frames()[1];
let waitError = null; let waitError = null;
@ -276,7 +340,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(waitError).toBeTruthy(); expect(waitError).toBeTruthy();
expect(waitError.message).toContain('waitForFunction failed: frame got detached.'); expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
}); });
it('should survive cross-process navigation', async({page, server}) => { it('should survive cross-process navigation', async() => {
const { page, server } = getTestState();
let boxFound = false; let boxFound = false;
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true); const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
@ -287,7 +353,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitForSelector; await waitForSelector;
expect(boxFound).toBe(true); expect(boxFound).toBe(true);
}); });
it('should wait for visible', async({page, server}) => { it('should wait for visible', async() => {
const { page } = getTestState();
let divFound = false; let divFound = false;
const waitForSelector = page.waitForSelector('div', {visible: true}).then(() => divFound = true); const waitForSelector = page.waitForSelector('div', {visible: true}).then(() => divFound = true);
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`); await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
@ -298,7 +366,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForSelector).toBe(true); expect(await waitForSelector).toBe(true);
expect(divFound).toBe(true); expect(divFound).toBe(true);
}); });
it('should wait for visible recursively', async({page, server}) => { it('should wait for visible recursively', async() => {
const { page } = getTestState();
let divVisible = false; let divVisible = false;
const waitForSelector = page.waitForSelector('div#inner', {visible: true}).then(() => divVisible = true); const waitForSelector = page.waitForSelector('div#inner', {visible: true}).then(() => divVisible = true);
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`); await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
@ -309,7 +379,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForSelector).toBe(true); expect(await waitForSelector).toBe(true);
expect(divVisible).toBe(true); expect(divVisible).toBe(true);
}); });
it('hidden should wait for visibility: hidden', async({page, server}) => { it('hidden should wait for visibility: hidden', async() => {
const { page } = getTestState();
let divHidden = false; let divHidden = false;
await page.setContent(`<div style='display: block;'></div>`); await page.setContent(`<div style='display: block;'></div>`);
const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true); const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true);
@ -319,7 +391,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForSelector).toBe(true); expect(await waitForSelector).toBe(true);
expect(divHidden).toBe(true); expect(divHidden).toBe(true);
}); });
it('hidden should wait for display: none', async({page, server}) => { it('hidden should wait for display: none', async() => {
const { page } = getTestState();
let divHidden = false; let divHidden = false;
await page.setContent(`<div style='display: block;'></div>`); await page.setContent(`<div style='display: block;'></div>`);
const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true); const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divHidden = true);
@ -329,7 +403,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForSelector).toBe(true); expect(await waitForSelector).toBe(true);
expect(divHidden).toBe(true); expect(divHidden).toBe(true);
}); });
it('hidden should wait for removal', async({page, server}) => { it('hidden should wait for removal', async() => {
const { page } = getTestState();
await page.setContent(`<div></div>`); await page.setContent(`<div></div>`);
let divRemoved = false; let divRemoved = false;
const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divRemoved = true); const waitForSelector = page.waitForSelector('div', {hidden: true}).then(() => divRemoved = true);
@ -339,18 +415,24 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForSelector).toBe(true); expect(await waitForSelector).toBe(true);
expect(divRemoved).toBe(true); expect(divRemoved).toBe(true);
}); });
it('should return null if waiting to hide non-existing element', async({page, server}) => { it('should return null if waiting to hide non-existing element', async() => {
const { page } = getTestState();
const handle = await page.waitForSelector('non-existing', { hidden: true }); const handle = await page.waitForSelector('non-existing', { hidden: true });
expect(handle).toBe(null); expect(handle).toBe(null);
}); });
it('should respect timeout', async({page, server}) => { it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null; let error = null;
await page.waitForSelector('div', {timeout: 10}).catch(e => error = e); await page.waitForSelector('div', {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('waiting for selector "div" failed: timeout'); expect(error.message).toContain('waiting for selector "div" failed: timeout');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
}); });
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => { it('should have an error message specifically for awaiting an element to be hidden', async() => {
const { page } = getTestState();
await page.setContent(`<div></div>`); await page.setContent(`<div></div>`);
let error = null; let error = null;
await page.waitForSelector('div', {hidden: true, timeout: 10}).catch(e => error = e); await page.waitForSelector('div', {hidden: true, timeout: 10}).catch(e => error = e);
@ -358,7 +440,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(error.message).toContain('waiting for selector "div" to be hidden failed: timeout'); expect(error.message).toContain('waiting for selector "div" to be hidden failed: timeout');
}); });
it('should respond to node attribute mutation', async({page, server}) => { it('should respond to node attribute mutation', async() => {
const { page } = getTestState();
let divFound = false; let divFound = false;
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true); const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
await page.setContent(`<div class='notZombo'></div>`); await page.setContent(`<div class='notZombo'></div>`);
@ -366,34 +450,44 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await page.evaluate(() => document.querySelector('div').className = 'zombo'); await page.evaluate(() => document.querySelector('div').className = 'zombo');
expect(await waitForSelector).toBe(true); expect(await waitForSelector).toBe(true);
}); });
it_fails_ffox('should return the element handle', async({page, server}) => { itFailsFirefox('should return the element handle', async() => {
const { page } = getTestState();
const waitForSelector = page.waitForSelector('.zombo'); const waitForSelector = page.waitForSelector('.zombo');
await page.setContent(`<div class='zombo'>anything</div>`); await page.setContent(`<div class='zombo'>anything</div>`);
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything'); expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
}); });
it('should have correct stack trace for timeout', async({page, server}) => { it('should have correct stack trace for timeout', async() => {
const { page } = getTestState();
let error; let error;
await page.waitForSelector('.zombo', {timeout: 10}).catch(e => error = e); await page.waitForSelector('.zombo', {timeout: 10}).catch(e => error = e);
expect(error.stack).toContain('waittask.spec.js'); expect(error.stack).toContain('waittask.spec.js');
}); });
}); });
describe_fails_ffox('Frame.waitForXPath', function() { describeFailsFirefox('Frame.waitForXPath', function() {
const addElement = tag => document.body.appendChild(document.createElement(tag)); const addElement = tag => document.body.appendChild(document.createElement(tag));
it('should support some fancy xpath', async({page, server}) => { it('should support some fancy xpath', async() => {
const { page } = getTestState();
await page.setContent(`<p>red herring</p><p>hello world </p>`); await page.setContent(`<p>red herring</p><p>hello world </p>`);
const waitForXPath = page.waitForXPath('//p[normalize-space(.)="hello world"]'); const waitForXPath = page.waitForXPath('//p[normalize-space(.)="hello world"]');
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world '); expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
}); });
it('should respect timeout', async({page}) => { it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null; let error = null;
await page.waitForXPath('//div', {timeout: 10}).catch(e => error = e); await page.waitForXPath('//div', {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy(); expect(error).toBeTruthy();
expect(error.message).toContain('waiting for XPath "//div" failed: timeout'); expect(error.message).toContain('waiting for XPath "//div" failed: timeout');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError); expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
}); });
it('should run in specified frame', async({page, server}) => { it('should run in specified frame', async() => {
const { page, server } = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1]; const frame1 = page.frames()[1];
@ -404,7 +498,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
const eHandle = await waitForXPathPromise; const eHandle = await waitForXPathPromise;
expect(eHandle.executionContext().frame()).toBe(frame2); expect(eHandle.executionContext().frame()).toBe(frame2);
}); });
it('should throw when frame is detached', async({page, server}) => { it('should throw when frame is detached', async() => {
const { page, server } = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1]; const frame = page.frames()[1];
let waitError = null; let waitError = null;
@ -414,7 +510,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(waitError).toBeTruthy(); expect(waitError).toBeTruthy();
expect(waitError.message).toContain('waitForFunction failed: frame got detached.'); expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
}); });
it('hidden should wait for display: none', async({page, server}) => { it('hidden should wait for display: none', async() => {
const { page } = getTestState();
let divHidden = false; let divHidden = false;
await page.setContent(`<div style='display: block;'></div>`); await page.setContent(`<div style='display: block;'></div>`);
const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true); const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true);
@ -424,21 +522,27 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForXPath).toBe(true); expect(await waitForXPath).toBe(true);
expect(divHidden).toBe(true); expect(divHidden).toBe(true);
}); });
it('should return the element handle', async({page, server}) => { it('should return the element handle', async() => {
const { page } = getTestState();
const waitForXPath = page.waitForXPath('//*[@class="zombo"]'); const waitForXPath = page.waitForXPath('//*[@class="zombo"]');
await page.setContent(`<div class='zombo'>anything</div>`); await page.setContent(`<div class='zombo'>anything</div>`);
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything'); expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
}); });
it('should allow you to select a text node', async({page, server}) => { it('should allow you to select a text node', async() => {
const { page } = getTestState();
await page.setContent(`<div>some text</div>`); await page.setContent(`<div>some text</div>`);
const text = await page.waitForXPath('//div/text()'); const text = await page.waitForXPath('//div/text()');
expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */); expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */);
}); });
it('should allow you to select an element with single slash', async({page, server}) => { it('should allow you to select an element with single slash', async() => {
const { page } = getTestState();
await page.setContent(`<div>some text</div>`); await page.setContent(`<div>some text</div>`);
const waitForXPath = page.waitForXPath('/html/body/div'); const waitForXPath = page.waitForXPath('/html/body/div');
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text'); expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
}); });
}); });
}; });

View File

@ -1,66 +1,92 @@
/**
* 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 expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const utils = require('./utils'); const utils = require('./utils');
const {waitEvent} = utils; const {waitEvent} = utils;
module.exports.addTests = function({testRunner, expect}) { describeFailsFirefox('Workers', function() {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner; setupTestBrowserHooks();
const {it, fit, xit} = testRunner; setupTestPageAndContextHooks();
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; it('Page.workers', async() => {
const { page, server } = getTestState();
describe_fails_ffox('Workers', function() { await Promise.all([
it('Page.workers', async function({page, server}) { new Promise(x => page.once('workercreated', x)),
await Promise.all([ page.goto(server.PREFIX + '/worker/worker.html')]);
new Promise(x => page.once('workercreated', x)), const worker = page.workers()[0];
page.goto(server.PREFIX + '/worker/worker.html')]); expect(worker.url()).toContain('worker.js');
const worker = page.workers()[0];
expect(worker.url()).toContain('worker.js');
expect(await worker.evaluate(() => self.workerFunction())).toBe('worker function result'); expect(await worker.evaluate(() => self.workerFunction())).toBe('worker function result');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
expect(page.workers().length).toBe(0); expect(page.workers().length).toBe(0);
}); });
it('should emit created and destroyed events', async function({page}) { it('should emit created and destroyed events', async() => {
const workerCreatedPromise = new Promise(x => page.once('workercreated', x)); const { page } = getTestState();
const workerObj = await page.evaluateHandle(() => new Worker('data:text/javascript,1'));
const worker = await workerCreatedPromise; const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
const workerThisObj = await worker.evaluateHandle(() => this); const workerObj = await page.evaluateHandle(() => new Worker('data:text/javascript,1'));
const workerDestroyedPromise = new Promise(x => page.once('workerdestroyed', x)); const worker = await workerCreatedPromise;
await page.evaluate(workerObj => workerObj.terminate(), workerObj); const workerThisObj = await worker.evaluateHandle(() => this);
expect(await workerDestroyedPromise).toBe(worker); const workerDestroyedPromise = new Promise(x => page.once('workerdestroyed', x));
const error = await workerThisObj.getProperty('self').catch(error => error); await page.evaluate(workerObj => workerObj.terminate(), workerObj);
expect(error.message).toContain('Most likely the worker has been closed.'); expect(await workerDestroyedPromise).toBe(worker);
}); const error = await workerThisObj.getProperty('self').catch(error => error);
it('should report console logs', async function({page}) { expect(error.message).toContain('Most likely the worker has been closed.');
const [message] = await Promise.all([ });
waitEvent(page, 'console'), it('should report console logs', async() => {
page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`)), const { page } = getTestState();
]);
expect(message.text()).toBe('1'); const [message] = await Promise.all([
expect(message.location()).toEqual({ waitEvent(page, 'console'),
url: 'data:text/javascript,console.log(1)', page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`)),
lineNumber: 0, ]);
columnNumber: 8, expect(message.text()).toBe('1');
}); expect(message.location()).toEqual({
}); url: 'data:text/javascript,console.log(1)',
it('should have JSHandles for console logs', async function({page}) { lineNumber: 0,
const logPromise = new Promise(x => page.on('console', x)); columnNumber: 8,
await page.evaluate(() => new Worker(`data:text/javascript,console.log(1,2,3,this)`));
const log = await logPromise;
expect(log.text()).toBe('1 2 3 JSHandle@object');
expect(log.args().length).toBe(4);
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
});
it('should have an execution context', async function({page}) {
const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
await page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`));
const worker = await workerCreatedPromise;
expect(await (await worker.executionContext()).evaluate('1+1')).toBe(2);
});
it('should report errors', async function({page}) {
const errorPromise = new Promise(x => page.on('pageerror', x));
await page.evaluate(() => new Worker(`data:text/javascript, throw new Error('this is my error');`));
const errorLog = await errorPromise;
expect(errorLog.message).toContain('this is my error');
}); });
}); });
}; it('should have JSHandles for console logs', async() => {
const { page } = getTestState();
const logPromise = new Promise(x => page.on('console', x));
await page.evaluate(() => new Worker(`data:text/javascript,console.log(1,2,3,this)`));
const log = await logPromise;
expect(log.text()).toBe('1 2 3 JSHandle@object');
expect(log.args().length).toBe(4);
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
});
it('should have an execution context', async() => {
const { page } = getTestState();
const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
await page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`));
const worker = await workerCreatedPromise;
expect(await (await worker.executionContext()).evaluate('1+1')).toBe(2);
});
it('should report errors', async() => {
const { page } = getTestState();
const errorPromise = new Promise(x => page.on('pageerror', x));
await page.evaluate(() => new Worker(`data:text/javascript, throw new Error('this is my error');`));
const errorLog = await errorPromise;
expect(errorLog.message).toContain('this is my error');
});
});