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

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"
},
"scripts": {
"unit": "node test/test.js",
"funit": "PUPPETEER_PRODUCT=firefox node test/test.js",
"unit": "mocha --config .mocharc.js",
"coverage": "cross-env COVERAGE=1 npm run unit",
"funit": "PUPPETEER_PRODUCT=firefox npm run unit",
"debug-unit": "node --inspect-brk test/test.js",
"test-doclint": "node utils/doclint/check_public_api/test/test.js && node utils/doclint/preprocessor/test.js",
"test": "npm run tsc && npm run lint --silent && npm run coverage && npm run test-doclint && npm run test-types && node utils/testrunner/test/test.js",
@ -22,7 +23,6 @@
"install": "node install.js",
"lint": "([ \"$CI\" = true ] && eslint --quiet -f codeframe . || eslint .) && npm run tsc && npm run doc",
"doc": "node utils/doclint/cli.js",
"coverage": "cross-env COVERAGE=true npm run unit",
"tsc": "tsc --version && tsc -p . && cp src/protocol.d.ts lib/ && cp src/externs.d.ts lib/",
"apply-next-version": "node utils/apply_next_version.js",
"bundle": "npm run tsc && npx browserify -r ./index.js:puppeteer -o utils/browser/puppeteer-web.js",
@ -57,10 +57,12 @@
"commonmark": "^0.28.1",
"cross-env": "^5.0.5",
"eslint": "^6.8.0",
"eslint-plugin-mocha": "^6.3.0",
"esprima": "^4.0.0",
"expect": "^25.2.7",
"jpeg-js": "^0.3.4",
"minimist": "^1.2.0",
"mocha": "^7.1.1",
"ncp": "^2.0.0",
"pixelmatch": "^4.0.2",
"pngjs": "^3.3.3",

View File

@ -15,69 +15,78 @@
*/
const {waitEvent} = require('./utils');
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describeChromeOnly('Target.createCDPSession', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Target.createCDPSession', function() {
it('should work', async function({page, server}) {
const client = await page.target().createCDPSession();
it('should work', async() => {
const { page } = getTestState();
await Promise.all([
client.send('Runtime.enable'),
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
]);
const foo = await page.evaluate(() => window.foo);
expect(foo).toBe('bar');
});
it('should send events', async function({page, server}) {
const client = await page.target().createCDPSession();
await client.send('Network.enable');
const events = [];
client.on('Network.requestWillBeSent', event => events.push(event));
await page.goto(server.EMPTY_PAGE);
expect(events.length).toBe(1);
});
it('should enable and disable domains independently', async function({page, server}) {
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain.
await page.coverage.startJSCoverage();
await page.coverage.stopJSCoverage();
// generate a script in page and wait for the event.
const [event] = await Promise.all([
waitEvent(client, 'Debugger.scriptParsed'),
page.evaluate('//# sourceURL=foo.js')
]);
// expect events to be dispatched.
expect(event.url).toBe('foo.js');
});
it('should be able to detach session', async function({page, server}) {
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await client.detach();
let error = null;
try {
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
} catch (e) {
error = e;
}
expect(error.message).toContain('Session closed.');
});
it('should throw nice errors', async function({page}) {
const client = await page.target().createCDPSession();
const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist');
const client = await page.target().createCDPSession();
async function theSourceOfTheProblems() {
await client.send('ThisCommand.DoesNotExist');
}
});
await Promise.all([
client.send('Runtime.enable'),
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
]);
const foo = await page.evaluate(() => window.foo);
expect(foo).toBe('bar');
});
};
it('should send events', async() => {
const { page, server } = getTestState();
const client = await page.target().createCDPSession();
await client.send('Network.enable');
const events = [];
client.on('Network.requestWillBeSent', event => events.push(event));
await page.goto(server.EMPTY_PAGE);
expect(events.length).toBe(1);
});
it('should enable and disable domains independently', async() => {
const { page } = getTestState();
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain.
await page.coverage.startJSCoverage();
await page.coverage.stopJSCoverage();
// generate a script in page and wait for the event.
const [event] = await Promise.all([
waitEvent(client, 'Debugger.scriptParsed'),
page.evaluate('//# sourceURL=foo.js')
]);
// expect events to be dispatched.
expect(event.url).toBe('foo.js');
});
it('should be able to detach session', async() => {
const { page } = getTestState();
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await client.detach();
let error = null;
try {
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
} catch (e) {
error = e;
}
expect(error.message).toContain('Session closed.');
});
it('should throw nice errors', async() => {
const { page } = getTestState();
const client = await page.target().createCDPSession();
const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist');
async function theSourceOfTheProblems() {
await client.send('ThisCommand.DoesNotExist');
}
});
});

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

View File

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

View File

@ -14,143 +14,160 @@
* limitations under the License.
*/
const expect = require('expect');
const {getTestState, setupTestBrowserHooks} = require('./mocha-utils');
const utils = require('./utils');
module.exports.addTests = function({testRunner, expect, puppeteer}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('BrowserContext', function() {
it_fails_ffox('should have default context', async function({browser, server}) {
expect(browser.browserContexts().length).toBe(1);
const defaultContext = browser.browserContexts()[0];
expect(defaultContext.isIncognito()).toBe(false);
let error = null;
await defaultContext.close().catch(e => error = e);
expect(browser.defaultBrowserContext()).toBe(defaultContext);
expect(error.message).toContain('cannot be closed');
});
it_fails_ffox('should create new incognito context', async function({browser, server}) {
expect(browser.browserContexts().length).toBe(1);
const context = await browser.createIncognitoBrowserContext();
expect(context.isIncognito()).toBe(true);
expect(browser.browserContexts().length).toBe(2);
expect(browser.browserContexts().indexOf(context) !== -1).toBe(true);
await context.close();
expect(browser.browserContexts().length).toBe(1);
});
it_fails_ffox('should close all belonging targets once closing context', async function({browser, server}) {
expect((await browser.pages()).length).toBe(1);
const context = await browser.createIncognitoBrowserContext();
await context.newPage();
expect((await browser.pages()).length).toBe(2);
expect((await context.pages()).length).toBe(1);
await context.close();
expect((await browser.pages()).length).toBe(1);
});
it_fails_ffox('window.open should use parent tab context', async function({browser, server}) {
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
const [popupTarget] = await Promise.all([
utils.waitEvent(browser, 'targetcreated'),
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
]);
expect(popupTarget.browserContext()).toBe(context);
await context.close();
});
it_fails_ffox('should fire target events', async function({browser, server}) {
const context = await browser.createIncognitoBrowserContext();
const events = [];
context.on('targetcreated', target => events.push('CREATED: ' + target.url()));
context.on('targetchanged', target => events.push('CHANGED: ' + target.url()));
context.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url()));
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
await page.close();
expect(events).toEqual([
'CREATED: about:blank',
`CHANGED: ${server.EMPTY_PAGE}`,
`DESTROYED: ${server.EMPTY_PAGE}`
]);
await context.close();
});
it_fails_ffox('should wait for a target', async function({browser, server}) {
const context = await browser.createIncognitoBrowserContext();
let resolved = false;
const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE);
targetPromise.then(() => resolved = true);
const page = await context.newPage();
expect(resolved).toBe(false);
await page.goto(server.EMPTY_PAGE);
const target = await targetPromise;
expect(await target.page()).toBe(page);
await context.close();
});
it('should timeout waiting for a non-existent target', async function({browser, server}) {
const context = await browser.createIncognitoBrowserContext();
const error = await context.waitForTarget(target => target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
await context.close();
});
it_fails_ffox('should isolate localStorage and cookies', async function({browser, server}) {
// Create two incognito contexts.
const context1 = await browser.createIncognitoBrowserContext();
const context2 = await browser.createIncognitoBrowserContext();
expect(context1.targets().length).toBe(0);
expect(context2.targets().length).toBe(0);
// Create a page in first incognito context.
const page1 = await context1.newPage();
await page1.goto(server.EMPTY_PAGE);
await page1.evaluate(() => {
localStorage.setItem('name', 'page1');
document.cookie = 'name=page1';
});
expect(context1.targets().length).toBe(1);
expect(context2.targets().length).toBe(0);
// Create a page in second incognito context.
const page2 = await context2.newPage();
await page2.goto(server.EMPTY_PAGE);
await page2.evaluate(() => {
localStorage.setItem('name', 'page2');
document.cookie = 'name=page2';
});
expect(context1.targets().length).toBe(1);
expect(context1.targets()[0]).toBe(page1.target());
expect(context2.targets().length).toBe(1);
expect(context2.targets()[0]).toBe(page2.target());
// Make sure pages don't share localstorage or cookies.
expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe('page1');
expect(await page1.evaluate(() => document.cookie)).toBe('name=page1');
expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe('page2');
expect(await page2.evaluate(() => document.cookie)).toBe('name=page2');
// Cleanup contexts.
await Promise.all([
context1.close(),
context2.close()
]);
expect(browser.browserContexts().length).toBe(1);
});
it_fails_ffox('should work across sessions', async function({browser, server}) {
expect(browser.browserContexts().length).toBe(1);
const context = await browser.createIncognitoBrowserContext();
expect(browser.browserContexts().length).toBe(2);
const remoteBrowser = await puppeteer.connect({
browserWSEndpoint: browser.wsEndpoint()
});
const contexts = remoteBrowser.browserContexts();
expect(contexts.length).toBe(2);
remoteBrowser.disconnect();
await context.close();
});
describe('BrowserContext', function() {
setupTestBrowserHooks();
itFailsFirefox('should have default context', async() => {
const { browser } = getTestState();
expect(browser.browserContexts().length).toEqual(1);
const defaultContext = browser.browserContexts()[0];
expect(defaultContext.isIncognito()).toBe(false);
let error = null;
await defaultContext.close().catch(e => error = e);
expect(browser.defaultBrowserContext()).toBe(defaultContext);
expect(error.message).toContain('cannot be closed');
});
};
itFailsFirefox('should create new incognito context', async() => {
const { browser } = getTestState();
expect(browser.browserContexts().length).toBe(1);
const context = await browser.createIncognitoBrowserContext();
expect(context.isIncognito()).toBe(true);
expect(browser.browserContexts().length).toBe(2);
expect(browser.browserContexts().indexOf(context) !== -1).toBe(true);
await context.close();
expect(browser.browserContexts().length).toBe(1);
});
itFailsFirefox('should close all belonging targets once closing context', async() => {
const { browser } = getTestState();
expect((await browser.pages()).length).toBe(1);
const context = await browser.createIncognitoBrowserContext();
await context.newPage();
expect((await browser.pages()).length).toBe(2);
expect((await context.pages()).length).toBe(1);
await context.close();
expect((await browser.pages()).length).toBe(1);
});
itFailsFirefox('window.open should use parent tab context', async() => {
const { browser, server } = getTestState();
const context = await browser.createIncognitoBrowserContext();
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
const [popupTarget] = await Promise.all([
utils.waitEvent(browser, 'targetcreated'),
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
]);
expect(popupTarget.browserContext()).toBe(context);
await context.close();
});
itFailsFirefox('should fire target events', async() => {
const { browser, server } = getTestState();
const context = await browser.createIncognitoBrowserContext();
const events = [];
context.on('targetcreated', target => events.push('CREATED: ' + target.url()));
context.on('targetchanged', target => events.push('CHANGED: ' + target.url()));
context.on('targetdestroyed', target => events.push('DESTROYED: ' + target.url()));
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
await page.close();
expect(events).toEqual([
'CREATED: about:blank',
`CHANGED: ${server.EMPTY_PAGE}`,
`DESTROYED: ${server.EMPTY_PAGE}`
]);
await context.close();
});
itFailsFirefox('should wait for a target', async() => {
const { browser, server } = getTestState();
const context = await browser.createIncognitoBrowserContext();
let resolved = false;
const targetPromise = context.waitForTarget(target => target.url() === server.EMPTY_PAGE);
targetPromise.then(() => resolved = true);
const page = await context.newPage();
expect(resolved).toBe(false);
await page.goto(server.EMPTY_PAGE);
const target = await targetPromise;
expect(await target.page()).toBe(page);
await context.close();
});
it('should timeout waiting for a non-existent target', async() => {
const { browser, server, puppeteer } = getTestState();
const context = await browser.createIncognitoBrowserContext();
const error = await context.waitForTarget(target => target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
await context.close();
});
itFailsFirefox('should isolate localStorage and cookies', async() => {
const { browser, server } = getTestState();
// Create two incognito contexts.
const context1 = await browser.createIncognitoBrowserContext();
const context2 = await browser.createIncognitoBrowserContext();
expect(context1.targets().length).toBe(0);
expect(context2.targets().length).toBe(0);
// Create a page in first incognito context.
const page1 = await context1.newPage();
await page1.goto(server.EMPTY_PAGE);
await page1.evaluate(() => {
localStorage.setItem('name', 'page1');
document.cookie = 'name=page1';
});
expect(context1.targets().length).toBe(1);
expect(context2.targets().length).toBe(0);
// Create a page in second incognito context.
const page2 = await context2.newPage();
await page2.goto(server.EMPTY_PAGE);
await page2.evaluate(() => {
localStorage.setItem('name', 'page2');
document.cookie = 'name=page2';
});
expect(context1.targets().length).toBe(1);
expect(context1.targets()[0]).toBe(page1.target());
expect(context2.targets().length).toBe(1);
expect(context2.targets()[0]).toBe(page2.target());
// Make sure pages don't share localstorage or cookies.
expect(await page1.evaluate(() => localStorage.getItem('name'))).toBe('page1');
expect(await page1.evaluate(() => document.cookie)).toBe('name=page1');
expect(await page2.evaluate(() => localStorage.getItem('name'))).toBe('page2');
expect(await page2.evaluate(() => document.cookie)).toBe('name=page2');
// Cleanup contexts.
await Promise.all([
context1.close(),
context2.close()
]);
expect(browser.browserContexts().length).toBe(1);
});
itFailsFirefox('should work across sessions', async() => {
const { browser, puppeteer} = getTestState();
expect(browser.browserContexts().length).toBe(1);
const context = await browser.createIncognitoBrowserContext();
expect(browser.browserContexts().length).toBe(2);
const remoteBrowser = await puppeteer.connect({
browserWSEndpoint: browser.wsEndpoint()
});
const contexts = remoteBrowser.browserContexts();
expect(contexts.length).toBe(2);
remoteBrowser.disconnect();
await context.close();
});
});

View File

@ -13,116 +13,118 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addLauncherTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describeChromeOnly('Chromium-Specific Launcher tests', function() {
describe('Puppeteer.launch |browserURL| option', function() {
it('should be able to connect using browserUrl, with and without trailing slash', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
describe('Chromium-Specific Launcher tests', function() {
describe('Puppeteer.launch |browserURL| option', function() {
it('should be able to connect using browserUrl, with and without trailing slash', async({server}) => {
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:21222';
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:21222';
const browser1 = await puppeteer.connect({browserURL});
const page1 = await browser1.newPage();
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
browser1.disconnect();
const browser1 = await puppeteer.connect({browserURL});
const page1 = await browser1.newPage();
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
browser1.disconnect();
const browser2 = await puppeteer.connect({browserURL: browserURL + '/'});
const page2 = await browser2.newPage();
expect(await page2.evaluate(() => 8 * 7)).toBe(56);
browser2.disconnect();
originalBrowser.close();
});
it('should throw when using both browserWSEndpoint and browserURL', async({server}) => {
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:21222';
let error = null;
await puppeteer.connect({browserURL, browserWSEndpoint: originalBrowser.wsEndpoint()}).catch(e => error = e);
expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport');
originalBrowser.close();
});
it('should throw when trying to connect to non-existing browser', async({server}) => {
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:32333';
let error = null;
await puppeteer.connect({browserURL}).catch(e => error = e);
expect(error.message).toContain('Failed to fetch browser webSocket url from');
originalBrowser.close();
});
const browser2 = await puppeteer.connect({browserURL: browserURL + '/'});
const page2 = await browser2.newPage();
expect(await page2.evaluate(() => 8 * 7)).toBe(56);
browser2.disconnect();
originalBrowser.close();
});
it('should throw when using both browserWSEndpoint and browserURL', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
describe('Puppeteer.launch |pipe| option', function() {
it('should support the pipe option', async() => {
const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browser = await puppeteer.launch(options);
expect((await browser.pages()).length).toBe(1);
expect(browser.wsEndpoint()).toBe('');
const page = await browser.newPage();
expect(await page.evaluate('11 * 11')).toBe(121);
await page.close();
await browser.close();
});
it('should support the pipe argument', async() => {
const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
const browser = await puppeteer.launch(options);
expect(browser.wsEndpoint()).toBe('');
const page = await browser.newPage();
expect(await page.evaluate('11 * 11')).toBe(121);
await page.close();
await browser.close();
});
it('should fire "disconnected" when closing with pipe', async() => {
const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browser = await puppeteer.launch(options);
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
// Emulate user exiting browser.
browser.process().kill();
await disconnectedEventPromise;
});
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:21222';
let error = null;
await puppeteer.connect({browserURL, browserWSEndpoint: originalBrowser.wsEndpoint()}).catch(e => error = e);
expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport');
originalBrowser.close();
});
it('should throw when trying to connect to non-existing browser', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const originalBrowser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222']
}));
const browserURL = 'http://127.0.0.1:32333';
let error = null;
await puppeteer.connect({browserURL}).catch(e => error = e);
expect(error.message).toContain('Failed to fetch browser webSocket url from');
originalBrowser.close();
});
});
};
module.exports.addPageTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Puppeteer.launch |pipe| option', function() {
it('should support the pipe option', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browser = await puppeteer.launch(options);
expect((await browser.pages()).length).toBe(1);
expect(browser.wsEndpoint()).toBe('');
const page = await browser.newPage();
expect(await page.evaluate('11 * 11')).toBe(121);
await page.close();
await browser.close();
});
it('should support the pipe argument', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
const browser = await puppeteer.launch(options);
expect(browser.wsEndpoint()).toBe('');
const page = await browser.newPage();
expect(await page.evaluate('11 * 11')).toBe(121);
await page.close();
await browser.close();
});
it('should fire "disconnected" when closing with pipe', async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browser = await puppeteer.launch(options);
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
// Emulate user exiting browser.
browser.process().kill();
await disconnectedEventPromise;
});
});
describe('Chromium-Specific Page Tests', function() {
it('Page.setRequestInterception should work with intervention headers', async({server, page}) => {
server.setRoute('/intervention', (req, res) => res.end(`
});
describeChromeOnly('Chromium-Specific Page Tests', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('Page.setRequestInterception should work with intervention headers', async() => {
const { server, page } = getTestState();
server.setRoute('/intervention', (req, res) => res.end(`
<script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
</script>
`));
server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => {
serverRequest = req;
res.end('console.log(1);');
});
await page.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.PREFIX + '/intervention');
// Check for feature URL substring rather than https://www.chromestatus.com to
// make it work with Edgium.
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => {
serverRequest = req;
res.end('console.log(1);');
});
});
};
await page.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.PREFIX + '/intervention');
// Check for feature URL substring rather than https://www.chromestatus.com to
// make it work with Edgium.
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
});
});

View File

@ -14,37 +14,44 @@
* limitations under the License.
*/
const expect = require('expect');
const {getTestState,setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils');
const utils = require('./utils');
module.exports.addTests = function({testRunner, expect, puppeteer}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.click', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should click the button', async() => {
const { page, server } = getTestState();
describe('Page.click', function() {
it('should click the button', async({page, server}) => {
await page.goto(server.PREFIX + '/input/button.html');
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
it_fails_ffox('should click svg', async({page, server}) => {
await page.setContent(`
await page.goto(server.PREFIX + '/input/button.html');
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
itFailsFirefox('should click svg', async() => {
const { page } = getTestState();
await page.setContent(`
<svg height="100" width="100">
<circle onclick="javascript:window.__CLICKED=42" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
`);
await page.click('circle');
expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
});
it_fails_ffox('should click the button if window.Node is removed', async({page, server}) => {
await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => delete window.Node);
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
// @see https://github.com/puppeteer/puppeteer/issues/4281
it_fails_ffox('should click on a span with an inline element inside', async({page, server}) => {
await page.setContent(`
await page.click('circle');
expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
});
itFailsFirefox('should click the button if window.Node is removed', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => delete window.Node);
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
// @see https://github.com/puppeteer/puppeteer/issues/4281
itFailsFirefox('should click on a span with an inline element inside', async() => {
const { page } = getTestState();
await page.setContent(`
<style>
span::before {
content: 'q';
@ -52,34 +59,42 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
</style>
<span onclick='javascript:window.CLICKED=42'></span>
`);
await page.click('span');
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
});
it('should not throw UnhandledPromiseRejection when page closes', async({page, server}) => {
const newPage = await page.browser().newPage();
await Promise.all([
newPage.close(),
newPage.mouse.click(1, 2),
]).catch(e => {});
});
it('should click the button after navigation ', async({page, server}) => {
await page.goto(server.PREFIX + '/input/button.html');
await page.click('button');
await page.goto(server.PREFIX + '/input/button.html');
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
it_fails_ffox('should click with disabled javascript', async({page, server}) => {
await page.setJavaScriptEnabled(false);
await page.goto(server.PREFIX + '/wrappedlink.html');
await Promise.all([
page.click('a'),
page.waitForNavigation()
]);
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked');
});
it_fails_ffox('should click when one of inline box children is outside of viewport', async({page, server}) => {
await page.setContent(`
await page.click('span');
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
});
it('should not throw UnhandledPromiseRejection when page closes', async() => {
const { page } = getTestState();
const newPage = await page.browser().newPage();
await Promise.all([
newPage.close(),
newPage.mouse.click(1, 2),
]).catch(e => {});
});
it('should click the button after navigation ', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/button.html');
await page.click('button');
await page.goto(server.PREFIX + '/input/button.html');
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
itFailsFirefox('should click with disabled javascript', async() => {
const { page, server } = getTestState();
await page.setJavaScriptEnabled(false);
await page.goto(server.PREFIX + '/wrappedlink.html');
await Promise.all([
page.click('a'),
page.waitForNavigation()
]);
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked');
});
itFailsFirefox('should click when one of inline box children is outside of viewport', async() => {
const { page } = getTestState();
await page.setContent(`
<style>
i {
position: absolute;
@ -88,175 +103,206 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
</style>
<span onclick='javascript:window.CLICKED = 42;'><i>woof</i><b>doggo</b></span>
`);
await page.click('span');
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
});
it('should select the text by triple clicking', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
await page.keyboard.type(text);
await page.click('textarea');
await page.click('textarea', {clickCount: 2});
await page.click('textarea', {clickCount: 3});
expect(await page.evaluate(() => {
const textarea = document.querySelector('textarea');
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
})).toBe(text);
});
it_fails_ffox('should click offscreen buttons', async({page, server}) => {
await page.goto(server.PREFIX + '/offscreenbuttons.html');
const messages = [];
page.on('console', msg => messages.push(msg.text()));
for (let i = 0; i < 11; ++i) {
// We might've scrolled to click a button - reset to (0, 0).
await page.evaluate(() => window.scrollTo(0, 0));
await page.click(`#btn${i}`);
}
expect(messages).toEqual([
'button #0 clicked',
'button #1 clicked',
'button #2 clicked',
'button #3 clicked',
'button #4 clicked',
'button #5 clicked',
'button #6 clicked',
'button #7 clicked',
'button #8 clicked',
'button #9 clicked',
'button #10 clicked'
]);
});
it('should click wrapped links', async({page, server}) => {
await page.goto(server.PREFIX + '/wrappedlink.html');
await page.click('a');
expect(await page.evaluate(() => window.__clicked)).toBe(true);
});
it('should click on checkbox input and toggle', async({page, server}) => {
await page.goto(server.PREFIX + '/input/checkbox.html');
expect(await page.evaluate(() => result.check)).toBe(null);
await page.click('input#agree');
expect(await page.evaluate(() => result.check)).toBe(true);
expect(await page.evaluate(() => result.events)).toEqual([
'mouseover',
'mouseenter',
'mousemove',
'mousedown',
'mouseup',
'click',
'input',
'change',
]);
await page.click('input#agree');
expect(await page.evaluate(() => result.check)).toBe(false);
});
it_fails_ffox('should click on checkbox label and toggle', async({page, server}) => {
await page.goto(server.PREFIX + '/input/checkbox.html');
expect(await page.evaluate(() => result.check)).toBe(null);
await page.click('label[for="agree"]');
expect(await page.evaluate(() => result.check)).toBe(true);
expect(await page.evaluate(() => result.events)).toEqual([
'click',
'input',
'change',
]);
await page.click('label[for="agree"]');
expect(await page.evaluate(() => result.check)).toBe(false);
});
it('should fail to click a missing button', async({page, server}) => {
await page.goto(server.PREFIX + '/input/button.html');
let error = null;
await page.click('button.does-not-exist').catch(e => error = e);
expect(error.message).toBe('No node found for selector: button.does-not-exist');
});
// @see https://github.com/puppeteer/puppeteer/issues/161
it('should not hang with touch-enabled viewports', async({page, server}) => {
await page.setViewport(puppeteer.devices['iPhone 6'].viewport);
await page.mouse.down();
await page.mouse.move(100, 10);
await page.mouse.up();
});
it('should scroll and click the button', async({page, server}) => {
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.click('#button-5');
expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked');
await page.click('#button-80');
expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked');
});
it_fails_ffox('should double click the button', async({page, server}) => {
await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => {
window.double = false;
const button = document.querySelector('button');
button.addEventListener('dblclick', event => {
window.double = true;
});
});
const button = await page.$('button');
await button.click({ clickCount: 2 });
expect(await page.evaluate('double')).toBe(true);
expect(await page.evaluate('result')).toBe('Clicked');
});
it('should click a partially obscured button', async({page, server}) => {
await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => {
const button = document.querySelector('button');
button.textContent = 'Some really long text that will go offscreen';
button.style.position = 'absolute';
button.style.left = '368px';
});
await page.click('button');
expect(await page.evaluate(() => window.result)).toBe('Clicked');
});
it('should click a rotated button', async({page, server}) => {
await page.goto(server.PREFIX + '/input/rotatedButton.html');
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
it('should fire contextmenu event on right click', async({page, server}) => {
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.click('#button-8', {button: 'right'});
expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu');
});
// @see https://github.com/puppeteer/puppeteer/issues/206
it_fails_ffox('should click links which cause navigation', async({page, server}) => {
await page.setContent(`<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');
});
await page.click('span');
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
});
};
it('should select the text by triple clicking', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
await page.keyboard.type(text);
await page.click('textarea');
await page.click('textarea', {clickCount: 2});
await page.click('textarea', {clickCount: 3});
expect(await page.evaluate(() => {
const textarea = document.querySelector('textarea');
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
})).toBe(text);
});
itFailsFirefox('should click offscreen buttons', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/offscreenbuttons.html');
const messages = [];
page.on('console', msg => messages.push(msg.text()));
for (let i = 0; i < 11; ++i) {
// We might've scrolled to click a button - reset to (0, 0).
await page.evaluate(() => window.scrollTo(0, 0));
await page.click(`#btn${i}`);
}
expect(messages).toEqual([
'button #0 clicked',
'button #1 clicked',
'button #2 clicked',
'button #3 clicked',
'button #4 clicked',
'button #5 clicked',
'button #6 clicked',
'button #7 clicked',
'button #8 clicked',
'button #9 clicked',
'button #10 clicked'
]);
});
it('should click wrapped links', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/wrappedlink.html');
await page.click('a');
expect(await page.evaluate(() => window.__clicked)).toBe(true);
});
it('should click on checkbox input and toggle', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/checkbox.html');
expect(await page.evaluate(() => result.check)).toBe(null);
await page.click('input#agree');
expect(await page.evaluate(() => result.check)).toBe(true);
expect(await page.evaluate(() => result.events)).toEqual([
'mouseover',
'mouseenter',
'mousemove',
'mousedown',
'mouseup',
'click',
'input',
'change',
]);
await page.click('input#agree');
expect(await page.evaluate(() => result.check)).toBe(false);
});
itFailsFirefox('should click on checkbox label and toggle', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/checkbox.html');
expect(await page.evaluate(() => result.check)).toBe(null);
await page.click('label[for="agree"]');
expect(await page.evaluate(() => result.check)).toBe(true);
expect(await page.evaluate(() => result.events)).toEqual([
'click',
'input',
'change',
]);
await page.click('label[for="agree"]');
expect(await page.evaluate(() => result.check)).toBe(false);
});
it('should fail to click a missing button', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/button.html');
let error = null;
await page.click('button.does-not-exist').catch(e => error = e);
expect(error.message).toBe('No node found for selector: button.does-not-exist');
});
// @see https://github.com/puppeteer/puppeteer/issues/161
it('should not hang with touch-enabled viewports', async() => {
const { page, puppeteer } = getTestState();
await page.setViewport(puppeteer.devices['iPhone 6'].viewport);
await page.mouse.down();
await page.mouse.move(100, 10);
await page.mouse.up();
});
it('should scroll and click the button', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.click('#button-5');
expect(await page.evaluate(() => document.querySelector('#button-5').textContent)).toBe('clicked');
await page.click('#button-80');
expect(await page.evaluate(() => document.querySelector('#button-80').textContent)).toBe('clicked');
});
itFailsFirefox('should double click the button', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => {
window.double = false;
const button = document.querySelector('button');
button.addEventListener('dblclick', event => {
window.double = true;
});
});
const button = await page.$('button');
await button.click({ clickCount: 2 });
expect(await page.evaluate('double')).toBe(true);
expect(await page.evaluate('result')).toBe('Clicked');
});
it('should click a partially obscured button', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(() => {
const button = document.querySelector('button');
button.textContent = 'Some really long text that will go offscreen';
button.style.position = 'absolute';
button.style.left = '368px';
});
await page.click('button');
expect(await page.evaluate(() => window.result)).toBe('Clicked');
});
it('should click a rotated button', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/rotatedButton.html');
await page.click('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
it('should fire contextmenu event on right click', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.click('#button-8', {button: 'right'});
expect(await page.evaluate(() => document.querySelector('#button-8').textContent)).toBe('context menu');
});
// @see https://github.com/puppeteer/puppeteer/issues/206
itFailsFirefox('should click links which cause navigation', async() => {
const { page, server } = getTestState();
await page.setContent(`<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
* limitations under the License.
*/
const expect = require('expect');
const {getTestState, setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Cookie specs', () => {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Page.cookies', function() {
it('should return no cookies in pristine browser context', async({page, server}) => {
it('should return no cookies in pristine browser context', async() => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
expect(await page.cookies()).toEqual([]);
});
it_fails_ffox('should get a cookie', async({page, server}) => {
itFailsFirefox('should get a cookie', async() => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
document.cookie = 'username=John Doe';
@ -41,7 +44,8 @@ module.exports.addTests = function({testRunner, expect}) {
session: true
}]);
});
it('should properly report httpOnly cookie', async({page, server}) => {
it('should properly report httpOnly cookie', async() => {
const {page, server} = getTestState();
server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', ';HttpOnly; Path=/');
res.end();
@ -51,7 +55,8 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies.length).toBe(1);
expect(cookies[0].httpOnly).toBe(true);
});
it('should properly report "Strict" sameSite cookie', async({page, server}) => {
it('should properly report "Strict" sameSite cookie', async() => {
const {page, server} = getTestState();
server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', ';SameSite=Strict');
res.end();
@ -61,7 +66,8 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies.length).toBe(1);
expect(cookies[0].sameSite).toBe('Strict');
});
it('should properly report "Lax" sameSite cookie', async({page, server}) => {
it('should properly report "Lax" sameSite cookie', async() => {
const {page, server} = getTestState();
server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', ';SameSite=Lax');
res.end();
@ -71,7 +77,8 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies.length).toBe(1);
expect(cookies[0].sameSite).toBe('Lax');
});
it_fails_ffox('should get multiple cookies', async({page, server}) => {
itFailsFirefox('should get multiple cookies', async() => {
const {page, server} = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
document.cookie = 'username=John Doe';
@ -104,7 +111,8 @@ module.exports.addTests = function({testRunner, expect}) {
},
]);
});
it_fails_ffox('should get cookies from multiple urls', async({page, server}) => {
itFailsFirefox('should get cookies from multiple urls', async() => {
const {page} = getTestState();
await page.setCookie({
url: 'https://foo.com',
name: 'doggo',
@ -143,9 +151,10 @@ module.exports.addTests = function({testRunner, expect}) {
}]);
});
});
describe('Page.setCookie', function() {
it_fails_ffox('should work', async({page, server}) => {
itFailsFirefox('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'password',
@ -153,7 +162,9 @@ module.exports.addTests = function({testRunner, expect}) {
});
expect(await page.evaluate(() => document.cookie)).toEqual('password=123456');
});
it_fails_ffox('should isolate cookies in browser contexts', async({page, server, browser}) => {
itFailsFirefox('should isolate cookies in browser contexts', async() => {
const { page, server, browser } = getTestState();
const anotherContext = await browser.createIncognitoBrowserContext();
const anotherPage = await anotherContext.newPage();
@ -173,7 +184,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies2[0].value).toBe('page2value');
await anotherContext.close();
});
it_fails_ffox('should set multiple cookies', async({page, server}) => {
itFailsFirefox('should set multiple cookies', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'password',
@ -190,7 +203,9 @@ module.exports.addTests = function({testRunner, expect}) {
'password=123456',
]);
});
it_fails_ffox('should have |expires| set to |-1| for session cookies', async({page, server}) => {
itFailsFirefox('should have |expires| set to |-1| for session cookies', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'password',
@ -200,7 +215,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(cookies[0].session).toBe(true);
expect(cookies[0].expires).toBe(-1);
});
it_fails_ffox('should set cookie with reasonable defaults', async({page, server}) => {
itFailsFirefox('should set cookie with reasonable defaults', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'password',
@ -219,7 +236,9 @@ module.exports.addTests = function({testRunner, expect}) {
session: true
}]);
});
it_fails_ffox('should set a cookie with a path', async({page, server}) => {
itFailsFirefox('should set a cookie with a path', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/grid.html');
await page.setCookie({
name: 'gridcookie',
@ -244,7 +263,9 @@ module.exports.addTests = function({testRunner, expect}) {
await page.goto(server.PREFIX + '/grid.html');
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
});
it('should not set a cookie on a blank page', async function({page}) {
it('should not set a cookie on a blank page', async() => {
const { page } = getTestState();
await page.goto('about:blank');
let error = null;
try {
@ -254,7 +275,9 @@ module.exports.addTests = function({testRunner, expect}) {
}
expect(error.message).toContain('At least one of the url and domain needs to be specified');
});
it('should not set a cookie with blank page URL', async function({page, server}) {
it('should not set a cookie with blank page URL', async() => {
const { page, server } = getTestState();
let error = null;
await page.goto(server.EMPTY_PAGE);
try {
@ -269,7 +292,9 @@ module.exports.addTests = function({testRunner, expect}) {
`Blank page can not have cookie "example-cookie-blank"`
);
});
it('should not set a cookie on a data URL page', async function({page}) {
it('should not set a cookie on a data URL page', async() => {
const { page } = getTestState();
let error = null;
await page.goto('data:,Hello%2C%20World!');
try {
@ -279,7 +304,9 @@ module.exports.addTests = function({testRunner, expect}) {
}
expect(error.message).toContain('At least one of the url and domain needs to be specified');
});
it_fails_ffox('should default to setting secure cookie for HTTPS websites', async({page, server}) => {
itFailsFirefox('should default to setting secure cookie for HTTPS websites', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
const SECURE_URL = 'https://example.com';
await page.setCookie({
@ -290,7 +317,9 @@ module.exports.addTests = function({testRunner, expect}) {
const [cookie] = await page.cookies(SECURE_URL);
expect(cookie.secure).toBe(true);
});
it_fails_ffox('should be able to set unsecure cookie for HTTP website', async({page, server}) => {
itFailsFirefox('should be able to set unsecure cookie for HTTP website', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
const HTTP_URL = 'http://example.com';
await page.setCookie({
@ -301,7 +330,9 @@ module.exports.addTests = function({testRunner, expect}) {
const [cookie] = await page.cookies(HTTP_URL);
expect(cookie.secure).toBe(false);
});
it_fails_ffox('should set a cookie on a different domain', async({page, server}) => {
itFailsFirefox('should set a cookie on a different domain', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
url: 'https://www.example.com',
@ -322,7 +353,9 @@ module.exports.addTests = function({testRunner, expect}) {
session: true
}]);
});
it_fails_ffox('should set cookies from a frame', async({page, server}) => {
itFailsFirefox('should set cookies from a frame', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/grid.html');
await page.setCookie({name: 'localhost-cookie', value: 'best'});
await page.evaluate(src => {
@ -365,7 +398,9 @@ module.exports.addTests = function({testRunner, expect}) {
});
describe('Page.deleteCookie', function() {
it_fails_ffox('should work', async({page, server}) => {
itFailsFirefox('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'cookie1',
@ -382,4 +417,4 @@ module.exports.addTests = function({testRunner, expect}) {
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie3=3');
});
});
};
});

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

View File

@ -13,81 +13,76 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const expect = require('expect');
const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('DefaultBrowserContext', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
itFailsFirefox('page.cookies() should work', async() => {
const { page, server } = getTestState();
describe('DefaultBrowserContext', function() {
beforeEach(async state => {
state.browser = await puppeteer.launch(defaultBrowserOptions);
state.page = await state.browser.newPage();
});
afterEach(async state => {
await state.browser.close();
delete state.browser;
delete state.page;
});
it_fails_ffox('page.cookies() should work', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
document.cookie = 'username=John Doe';
});
expect(await page.cookies()).toEqual([{
name: 'username',
value: 'John Doe',
domain: 'localhost',
path: '/',
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true
}]);
});
it_fails_ffox('page.setCookie() should work', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'username',
value: 'John Doe'
});
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
expect(await page.cookies()).toEqual([{
name: 'username',
value: 'John Doe',
domain: 'localhost',
path: '/',
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true
}]);
});
it_fails_ffox('page.deleteCookie() should work', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'cookie1',
value: '1'
}, {
name: 'cookie2',
value: '2'
});
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
await page.deleteCookie({name: 'cookie2'});
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
expect(await page.cookies()).toEqual([{
name: 'cookie1',
value: '1',
domain: 'localhost',
path: '/',
expires: -1,
size: 8,
httpOnly: false,
secure: false,
session: true
}]);
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
document.cookie = 'username=John Doe';
});
expect(await page.cookies()).toEqual([{
name: 'username',
value: 'John Doe',
domain: 'localhost',
path: '/',
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true
}]);
});
};
itFailsFirefox('page.setCookie() should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'username',
value: 'John Doe'
});
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
expect(await page.cookies()).toEqual([{
name: 'username',
value: 'John Doe',
domain: 'localhost',
path: '/',
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true
}]);
});
itFailsFirefox('page.deleteCookie() should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setCookie({
name: 'cookie1',
value: '1'
}, {
name: 'cookie2',
value: '2'
});
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
await page.deleteCookie({name: 'cookie2'});
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
expect(await page.cookies()).toEqual([{
name: 'cookie1',
value: '1',
domain: 'localhost',
path: '/',
expires: -1,
size: 8,
httpOnly: false,
secure: false,
session: true
}]);
});
});

View File

@ -13,38 +13,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const expect = require('expect');
const {getTestState,setupTestPageAndContextHooks, setupTestBrowserHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Page.Events.Dialog', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should fire', async() => {
const { page } = getTestState();
describe('Page.Events.Dialog', function() {
it('should fire', async({page, server}) => {
page.on('dialog', dialog => {
expect(dialog.type()).toBe('alert');
expect(dialog.defaultValue()).toBe('');
expect(dialog.message()).toBe('yo');
dialog.accept();
});
await page.evaluate(() => alert('yo'));
});
it_fails_ffox('should allow accepting prompts', async({page, server}) => {
page.on('dialog', dialog => {
expect(dialog.type()).toBe('prompt');
expect(dialog.defaultValue()).toBe('yes.');
expect(dialog.message()).toBe('question?');
dialog.accept('answer!');
});
const result = await page.evaluate(() => prompt('question?', 'yes.'));
expect(result).toBe('answer!');
});
it('should dismiss the prompt', async({page, server}) => {
page.on('dialog', dialog => {
dialog.dismiss();
});
const result = await page.evaluate(() => prompt('question?'));
expect(result).toBe(null);
page.on('dialog', dialog => {
expect(dialog.type()).toBe('alert');
expect(dialog.defaultValue()).toBe('');
expect(dialog.message()).toBe('yo');
dialog.accept();
});
await page.evaluate(() => alert('yo'));
});
};
itFailsFirefox('should allow accepting prompts', async() => {
const { page } = getTestState();
page.on('dialog', dialog => {
expect(dialog.type()).toBe('prompt');
expect(dialog.defaultValue()).toBe('yes.');
expect(dialog.message()).toBe('question?');
dialog.accept('answer!');
});
const result = await page.evaluate(() => prompt('question?', 'yes.'));
expect(result).toBe('answer!');
});
it('should dismiss the prompt', async() => {
const { page } = getTestState();
page.on('dialog', dialog => {
dialog.dismiss();
});
const result = await page.evaluate(() => prompt('question?'));
expect(result).toBe(null);
});
});

View File

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

View File

@ -14,21 +14,33 @@
* limitations under the License.
*/
module.exports.addTests = function({testRunner, expect, puppeteer}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const expect = require('expect');
const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const iPhone = puppeteer.devices['iPhone 6'];
const iPhoneLandscape = puppeteer.devices['iPhone 6 landscape'];
describe('Emulation', () => {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
let iPhone;
let iPhoneLandscape;
before(() => {
const {puppeteer} = getTestState();
iPhone = puppeteer.devices['iPhone 6'];
iPhoneLandscape = puppeteer.devices['iPhone 6 landscape'];
});
describe('Page.viewport', function() {
it('should get the proper viewport size', async({page, server}) => {
it('should get the proper viewport size', async() => {
const { page } = getTestState();
expect(page.viewport()).toEqual({width: 800, height: 600});
await page.setViewport({width: 123, height: 456});
expect(page.viewport()).toEqual({width: 123, height: 456});
});
it('should support mobile emulation', async({page, server}) => {
it('should support mobile emulation', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(800);
await page.setViewport(iPhone.viewport);
@ -36,7 +48,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
await page.setViewport({width: 400, height: 300});
expect(await page.evaluate(() => window.innerWidth)).toBe(400);
});
it_fails_ffox('should support touch emulation', async({page, server}) => {
itFailsFirefox('should support touch emulation', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
await page.setViewport(iPhone.viewport);
@ -58,19 +72,25 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
return promise;
}
});
it_fails_ffox('should be detectable by Modernizr', async({page, server}) => {
itFailsFirefox('should be detectable by Modernizr', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
await page.setViewport(iPhone.viewport);
await page.goto(server.PREFIX + '/detect-touch.html');
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
});
it_fails_ffox('should detect touch when applying viewport with touches', async({page, server}) => {
itFailsFirefox('should detect touch when applying viewport with touches', async() => {
const { page, server } = getTestState();
await page.setViewport({ width: 800, height: 600, hasTouch: true });
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
});
it_fails_ffox('should support landscape emulation', async({page, server}) => {
itFailsFirefox('should support landscape emulation', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => screen.orientation.type)).toBe('portrait-primary');
await page.setViewport(iPhoneLandscape.viewport);
@ -81,13 +101,17 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
describe('Page.emulate', function() {
it('should work', async({page, server}) => {
it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/mobile.html');
await page.emulate(iPhone);
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
});
it('should support clicking', async({page, server}) => {
it('should support clicking', async() => {
const { page, server } = getTestState();
await page.emulate(iPhone);
await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button');
@ -98,7 +122,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
describe('Page.emulateMedia [deprecated]', function() {
/* emulateMedia is deprecated in favour of emulateMediaType but we
/* emulateMedia is deprecated in favour of emulateMediaType but we
* don't want to remove it from Puppeteer just yet. We can't check
* that emulateMedia === emulateMediaType because when running tests
* with COVERAGE=1 the methods get rewritten. So instead we
@ -108,7 +132,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
* If you update these tests, you should update emulateMediaType's
* tests, and vice-versa.
*/
it_fails_ffox('should work', async({page, server}) => {
itFailsFirefox('should work', async() => {
const { page } = getTestState();
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
await page.emulateMedia('print');
@ -118,7 +144,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
});
it('should throw in case of bad argument', async({page, server}) => {
it('should throw in case of bad argument', async() => {
const { page } = getTestState();
let error = null;
await page.emulateMedia('bad').catch(e => error = e);
expect(error.message).toBe('Unsupported media type: bad');
@ -126,10 +154,12 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
describe('Page.emulateMediaType', function() {
/* NOTE! Updating these tests? Update the emulateMedia tests above
/* NOTE! Updating these tests? Update the emulateMedia tests above
* too (and see the big comment for why we have these duplicated).
*/
it_fails_ffox('should work', async({page, server}) => {
itFailsFirefox('should work', async() => {
const { page } = getTestState();
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
await page.emulateMediaType('print');
@ -139,7 +169,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
});
it('should throw in case of bad argument', async({page, server}) => {
it('should throw in case of bad argument', async() => {
const { page } = getTestState();
let error = null;
await page.emulateMediaType('bad').catch(e => error = e);
expect(error.message).toBe('Unsupported media type: bad');
@ -147,7 +179,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
describe('Page.emulateMediaFeatures', function() {
it_fails_ffox('should work', async({page, server}) => {
itFailsFirefox('should work', async() => {
const { page } = getTestState();
await page.emulateMediaFeatures([
{ name: 'prefers-reduced-motion', value: 'reduce' },
]);
@ -175,15 +209,19 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches)).toBe(false);
});
it('should throw in case of bad argument', async({page, server}) => {
it('should throw in case of bad argument', async() => {
const { page } = getTestState();
let error = null;
await page.emulateMediaFeatures([{ name: 'bad', value: '' }]).catch(e => error = e);
expect(error.message).toBe('Unsupported media feature: bad');
});
});
describe_fails_ffox('Page.emulateTimezone', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('Page.emulateTimezone', function() {
it('should work', async() => {
const { page } = getTestState();
page.evaluate(() => {
globalThis.date = new Date(1479579154987);
});
@ -200,7 +238,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
});
it('should throw for invalid timezone IDs', async({page, server}) => {
it('should throw for invalid timezone IDs', async() => {
const { page } = getTestState();
let error = null;
await page.emulateTimezone('Foo/Bar').catch(e => error = e);
expect(error.message).toBe('Invalid timezone ID: Foo/Bar');
@ -209,4 +249,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
});
};
});

View File

@ -15,59 +15,85 @@
*/
const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks, setupTestPageAndContextHooks} = require('./mocha-utils');
const bigint = typeof BigInt !== 'undefined';
module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Evaluation specs', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Page.evaluate', function() {
it('should work', async({page, server}) => {
it('should work', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => 7 * 3);
expect(result).toBe(21);
});
(bigint ? it_fails_ffox : xit)('should transfer BigInt', async({page, server}) => {
(bigint ? itFailsFirefox : xit)('should transfer BigInt', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a, BigInt(42));
expect(result).toBe(BigInt(42));
});
it_fails_ffox('should transfer NaN', async({page, server}) => {
itFailsFirefox('should transfer NaN', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a, NaN);
expect(Object.is(result, NaN)).toBe(true);
});
it_fails_ffox('should transfer -0', async({page, server}) => {
itFailsFirefox('should transfer -0', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a, -0);
expect(Object.is(result, -0)).toBe(true);
});
it_fails_ffox('should transfer Infinity', async({page, server}) => {
itFailsFirefox('should transfer Infinity', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a, Infinity);
expect(Object.is(result, Infinity)).toBe(true);
});
it_fails_ffox('should transfer -Infinity', async({page, server}) => {
itFailsFirefox('should transfer -Infinity', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a, -Infinity);
expect(Object.is(result, -Infinity)).toBe(true);
});
it('should transfer arrays', async({page, server}) => {
it('should transfer arrays', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a, [1, 2, 3]);
expect(result).toEqual([1,2,3]);
});
it('should transfer arrays as arrays, not objects', async({page, server}) => {
it('should transfer arrays as arrays, not objects', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => Array.isArray(a), [1, 2, 3]);
expect(result).toBe(true);
});
it('should modify global environment', async({page}) => {
it('should modify global environment', async() => {
const { page } = getTestState();
await page.evaluate(() => window.globalVar = 123);
expect(await page.evaluate('globalVar')).toBe(123);
});
it('should evaluate in the page context', async({page, server}) => {
it('should evaluate in the page context', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/global-var.html');
expect(await page.evaluate('globalVar')).toBe(123);
});
it_fails_ffox('should return undefined for objects with symbols', async({page, server}) => {
itFailsFirefox('should return undefined for objects with symbols', async() => {
const { page } = getTestState();
expect(await page.evaluate(() => [Symbol('foo4')])).toBe(undefined);
});
it('should work with function shorthands', async({page, server}) => {
it('should work with function shorthands', async() => {
const { page } = getTestState();
const a = {
sum(a, b) { return a + b; },
@ -76,11 +102,15 @@ module.exports.addTests = function({testRunner, expect}) {
expect(await page.evaluate(a.sum, 1, 2)).toBe(3);
expect(await page.evaluate(a.mult, 2, 4)).toBe(8);
});
it('should work with unicode chars', async({page, server}) => {
it('should work with unicode chars', async() => {
const { page } = getTestState();
const result = await page.evaluate(a => a['中文字符'], {'中文字符': 42});
expect(result).toBe(42);
});
it_fails_ffox('should throw when evaluation triggers reload', async({page, server}) => {
itFailsFirefox('should throw when evaluation triggers reload', async() => {
const { page } = getTestState();
let error = null;
await page.evaluate(() => {
location.reload();
@ -88,11 +118,15 @@ module.exports.addTests = function({testRunner, expect}) {
}).catch(e => error = e);
expect(error.message).toContain('Protocol error');
});
it('should await promise', async({page, server}) => {
it('should await promise', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => Promise.resolve(8 * 7));
expect(result).toBe(56);
});
it('should work right after framenavigated', async({page, server}) => {
it('should work right after framenavigated', async() => {
const { page, server } = getTestState();
let frameEvaluation = null;
page.on('framenavigated', async frame => {
frameEvaluation = frame.evaluate(() => 6 * 7);
@ -100,7 +134,9 @@ module.exports.addTests = function({testRunner, expect}) {
await page.goto(server.EMPTY_PAGE);
expect(await frameEvaluation).toBe(42);
});
it_fails_ffox('should work from-inside an exposed function', async({page, server}) => {
itFailsFirefox('should work from-inside an exposed function', async() => {
const { page } = getTestState();
// Setup inpage callback, which calls Page.evaluate
await page.exposeFunction('callController', async function(a, b) {
return await page.evaluate((a, b) => a * b, a, b);
@ -110,61 +146,87 @@ module.exports.addTests = function({testRunner, expect}) {
});
expect(result).toBe(27);
});
it('should reject promise with exception', async({page, server}) => {
it('should reject promise with exception', async() => {
const { page } = getTestState();
let error = null;
await page.evaluate(() => not_existing_object.property).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('not_existing_object');
});
it('should support thrown strings as error messages', async({page, server}) => {
it('should support thrown strings as error messages', async() => {
const { page } = getTestState();
let error = null;
await page.evaluate(() => { throw 'qwerty'; }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('qwerty');
});
it('should support thrown numbers as error messages', async({page, server}) => {
it('should support thrown numbers as error messages', async() => {
const { page } = getTestState();
let error = null;
await page.evaluate(() => { throw 100500; }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('100500');
});
it('should return complex objects', async({page, server}) => {
it('should return complex objects', async() => {
const { page } = getTestState();
const object = {foo: 'bar!'};
const result = await page.evaluate(a => a, object);
expect(result).not.toBe(object);
expect(result).toEqual(object);
});
(bigint ? it_fails_ffox : xit)('should return BigInt', async({page, server}) => {
(bigint ? itFailsFirefox : xit)('should return BigInt', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => BigInt(42));
expect(result).toBe(BigInt(42));
});
it_fails_ffox('should return NaN', async({page, server}) => {
itFailsFirefox('should return NaN', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => NaN);
expect(Object.is(result, NaN)).toBe(true);
});
it_fails_ffox('should return -0', async({page, server}) => {
itFailsFirefox('should return -0', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => -0);
expect(Object.is(result, -0)).toBe(true);
});
it_fails_ffox('should return Infinity', async({page, server}) => {
itFailsFirefox('should return Infinity', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => Infinity);
expect(Object.is(result, Infinity)).toBe(true);
});
it_fails_ffox('should return -Infinity', async({page, server}) => {
itFailsFirefox('should return -Infinity', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => -Infinity);
expect(Object.is(result, -Infinity)).toBe(true);
});
it('should accept "undefined" as one of multiple parameters', async({page, server}) => {
it('should accept "undefined" as one of multiple parameters', async() => {
const { page } = getTestState();
const result = await page.evaluate((a, b) => Object.is(a, undefined) && Object.is(b, 'foo'), undefined, 'foo');
expect(result).toBe(true);
});
it('should properly serialize null fields', async({page}) => {
it('should properly serialize null fields', async() => {
const { page } = getTestState();
expect(await page.evaluate(() => ({a: undefined}))).toEqual({});
});
it_fails_ffox('should return undefined for non-serializable objects', async({page, server}) => {
itFailsFirefox('should return undefined for non-serializable objects', async() => {
const { page } = getTestState();
expect(await page.evaluate(() => window)).toBe(undefined);
});
it_fails_ffox('should fail for circular object', async({page, server}) => {
itFailsFirefox('should fail for circular object', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => {
const a = {};
const b = {a};
@ -173,7 +235,9 @@ module.exports.addTests = function({testRunner, expect}) {
});
expect(result).toBe(undefined);
});
it_fails_ffox('should be able to throw a tricky error', async({page, server}) => {
itFailsFirefox('should be able to throw a tricky error', async() => {
const { page } = getTestState();
const windowHandle = await page.evaluateHandle(() => window);
const errorText = await windowHandle.jsonValue().catch(e => e.message);
const error = await page.evaluate(errorText => {
@ -181,25 +245,35 @@ module.exports.addTests = function({testRunner, expect}) {
}, errorText).catch(e => e);
expect(error.message).toContain(errorText);
});
it('should accept a string', async({page, server}) => {
it('should accept a string', async() => {
const { page } = getTestState();
const result = await page.evaluate('1 + 2');
expect(result).toBe(3);
});
it('should accept a string with semi colons', async({page, server}) => {
it('should accept a string with semi colons', async() => {
const { page } = getTestState();
const result = await page.evaluate('1 + 5;');
expect(result).toBe(6);
});
it('should accept a string with comments', async({page, server}) => {
it('should accept a string with comments', async() => {
const { page } = getTestState();
const result = await page.evaluate('2 + 5;\n// do some math!');
expect(result).toBe(7);
});
it_fails_ffox('should accept element handle as an argument', async({page, server}) => {
itFailsFirefox('should accept element handle as an argument', async() => {
const { page } = getTestState();
await page.setContent('<section>42</section>');
const element = await page.$('section');
const text = await page.evaluate(e => e.textContent, element);
expect(text).toBe('42');
});
it_fails_ffox('should throw if underlying element was disposed', async({page, server}) => {
itFailsFirefox('should throw if underlying element was disposed', async() => {
const { page } = getTestState();
await page.setContent('<section>39</section>');
const element = await page.$('section');
expect(element).toBeTruthy();
@ -208,7 +282,9 @@ module.exports.addTests = function({testRunner, expect}) {
await page.evaluate(e => e.textContent, element).catch(e => error = e);
expect(error.message).toContain('JSHandle is disposed');
});
it_fails_ffox('should throw if elementHandles are from other frames', async({page, server}) => {
itFailsFirefox('should throw if elementHandles are from other frames', async() => {
const { page, server } = getTestState();
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const bodyHandle = await page.frames()[1].$('body');
let error = null;
@ -216,7 +292,9 @@ module.exports.addTests = function({testRunner, expect}) {
expect(error).toBeTruthy();
expect(error.message).toContain('JSHandles can be evaluated only in the context they were created');
});
it_fails_ffox('should simulate a user gesture', async({page, server}) => {
itFailsFirefox('should simulate a user gesture', async() => {
const { page } = getTestState();
const result = await page.evaluate(() => {
document.body.appendChild(document.createTextNode('test'));
document.execCommand('selectAll');
@ -224,7 +302,9 @@ module.exports.addTests = function({testRunner, expect}) {
});
expect(result).toBe(true);
});
it_fails_ffox('should throw a nice error after a navigation', async({page, server}) => {
itFailsFirefox('should throw a nice error after a navigation', async() => {
const { page } = getTestState();
const executionContext = await page.mainFrame().executionContext();
await Promise.all([
@ -234,7 +314,9 @@ module.exports.addTests = function({testRunner, expect}) {
const error = await executionContext.evaluate(() => null).catch(e => e);
expect(error.message).toContain('navigation');
});
it_fails_ffox('should not throw an error when evaluation does a navigation', async({page, server}) => {
itFailsFirefox('should not throw an error when evaluation does a navigation', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/one-style.html');
const result = await page.evaluate(() => {
window.location = '/empty.html';
@ -242,11 +324,15 @@ module.exports.addTests = function({testRunner, expect}) {
});
expect(result).toEqual([42]);
});
it_fails_ffox('should transfer 100Mb of data from page to node.js', async({page, server}) => {
itFailsFirefox('should transfer 100Mb of data from page to node.js', async function() {
const { page } = getTestState();
const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a'));
expect(a.length).toBe(100 * 1024 * 1024);
});
it('should throw error with detailed information on exception inside promise ', async({page, server}) => {
it('should throw error with detailed information on exception inside promise ', async() => {
const { page } = getTestState();
let error = null;
await page.evaluate(() => new Promise(() => {
throw new Error('Error in promise');
@ -255,15 +341,19 @@ module.exports.addTests = function({testRunner, expect}) {
});
});
describe_fails_ffox('Page.evaluateOnNewDocument', function() {
it('should evaluate before anything else on the page', async({page, server}) => {
describeFailsFirefox('Page.evaluateOnNewDocument', function() {
it('should evaluate before anything else on the page', async() => {
const { page, server } = getTestState();
await page.evaluateOnNewDocument(function(){
window.injected = 123;
});
await page.goto(server.PREFIX + '/tamperable.html');
expect(await page.evaluate(() => window.result)).toBe(123);
});
it('should work with CSP', async({page, server}) => {
it('should work with CSP', async() => {
const { page, server } = getTestState();
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
await page.evaluateOnNewDocument(function(){
window.injected = 123;
@ -278,7 +368,9 @@ module.exports.addTests = function({testRunner, expect}) {
});
describe('Frame.evaluate', function() {
it_fails_ffox('should have different execution contexts', async({page, server}) => {
itFailsFirefox('should have different execution contexts', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(page.frames().length).toBe(2);
@ -287,13 +379,17 @@ module.exports.addTests = function({testRunner, expect}) {
expect(await page.frames()[0].evaluate(() => window.FOO)).toBe('foo');
expect(await page.frames()[1].evaluate(() => window.FOO)).toBe('bar');
});
it_fails_ffox('should have correct execution contexts', async({page, server}) => {
itFailsFirefox('should have correct execution contexts', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html');
expect(page.frames().length).toBe(2);
expect(await page.frames()[0].evaluate(() => document.body.textContent.trim())).toBe('');
expect(await page.frames()[1].evaluate(() => document.body.textContent.trim())).toBe(`Hi, I'm frame`);
});
it('should execute after cross-site navigation', async({page, server}) => {
it('should execute after cross-site navigation', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
const mainFrame = page.mainFrame();
expect(await mainFrame.evaluate(() => window.location.href)).toContain('localhost');
@ -301,4 +397,4 @@ module.exports.addTests = function({testRunner, expect}) {
expect(await mainFrame.evaluate(() => window.location.href)).toContain('127');
});
});
};
});

View File

@ -14,59 +14,62 @@
* limitations under the License.
*/
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
const path = require('path');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer, puppeteerPath}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Fixtures', function() {
itFailsFirefox('dumpio option should work with pipe option ', async() => {
const { defaultBrowserOptions, puppeteerPath } = getTestState();
describe('Fixtures', function() {
it_fails_ffox('dumpio option should work with pipe option ', async({server}) => {
let dumpioData = '';
const {spawn} = require('child_process');
const options = Object.assign({}, defaultBrowserOptions, {pipe: true, dumpio: true});
const res = spawn('node',
[path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]);
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
await new Promise(resolve => res.on('close', resolve));
expect(dumpioData).toContain('message from dumpio');
});
it('should dump browser process stderr', async({server}) => {
let dumpioData = '';
const {spawn} = require('child_process');
const options = Object.assign({}, defaultBrowserOptions, {dumpio: true});
const res = spawn('node',
[path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]);
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
await new Promise(resolve => res.on('close', resolve));
expect(dumpioData).toContain('DevTools listening on ws://');
});
it('should close the browser when the node process closes', async({ server }) => {
const {spawn, execSync} = require('child_process');
const options = Object.assign({}, defaultBrowserOptions, {
// Disable DUMPIO to cleanly read stdout.
dumpio: false,
});
const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), puppeteerPath, JSON.stringify(options)]);
let wsEndPointCallback;
const wsEndPointPromise = new Promise(x => wsEndPointCallback = x);
let output = '';
res.stdout.on('data', data => {
output += data;
if (output.indexOf('\n'))
wsEndPointCallback(output.substring(0, output.indexOf('\n')));
});
const browser = await puppeteer.connect({ browserWSEndpoint: await wsEndPointPromise });
const promises = [
new Promise(resolve => browser.once('disconnected', resolve)),
new Promise(resolve => res.on('close', resolve))
];
if (process.platform === 'win32')
execSync(`taskkill /pid ${res.pid} /T /F`);
else
process.kill(res.pid);
await Promise.all(promises);
});
let dumpioData = '';
const {spawn} = require('child_process');
const options = Object.assign({}, defaultBrowserOptions, {pipe: true, dumpio: true});
const res = spawn('node',
[path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]);
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
await new Promise(resolve => res.on('close', resolve));
expect(dumpioData).toContain('message from dumpio');
});
};
it('should dump browser process stderr', async() => {
const { defaultBrowserOptions, puppeteerPath} = getTestState();
let dumpioData = '';
const {spawn} = require('child_process');
const options = Object.assign({}, defaultBrowserOptions, {dumpio: true});
const res = spawn('node',
[path.join(__dirname, 'fixtures', 'dumpio.js'), puppeteerPath, JSON.stringify(options)]);
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
await new Promise(resolve => res.on('close', resolve));
expect(dumpioData).toContain('DevTools listening on ws://');
});
it('should close the browser when the node process closes', async() => {
const { defaultBrowserOptions, puppeteerPath, puppeteer} = getTestState();
const {spawn, execSync} = require('child_process');
const options = Object.assign({}, defaultBrowserOptions, {
// Disable DUMPIO to cleanly read stdout.
dumpio: false,
});
const res = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), puppeteerPath, JSON.stringify(options)]);
let wsEndPointCallback;
const wsEndPointPromise = new Promise(x => wsEndPointCallback = x);
let output = '';
res.stdout.on('data', data => {
output += data;
if (output.indexOf('\n'))
wsEndPointCallback(output.substring(0, output.indexOf('\n')));
});
const browser = await puppeteer.connect({ browserWSEndpoint: await wsEndPointPromise });
const promises = [
new Promise(resolve => browser.once('disconnected', resolve)),
new Promise(resolve => res.on('close', resolve))
];
if (process.platform === 'win32')
execSync(`taskkill /pid ${res.pid} /T /F`);
else
process.kill(res.pid);
await Promise.all(promises);
});
});

View File

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

View File

@ -17,37 +17,51 @@
const path = require('path');
const os = require('os');
const fs = require('fs');
const util = require('util');
const utils = require('./utils');
const {waitEvent} = utils;
const {promisify} = require('util');
const {waitEvent} = require('./utils');
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
const rmAsync = util.promisify(require('rimraf'));
const mkdtempAsync = util.promisify(fs.mkdtemp);
const rmAsync = promisify(require('rimraf'));
const mkdtempAsync = promisify(fs.mkdtemp);
const TMP_FOLDER = path.join(os.tmpdir(), 'pptr_tmp_folder-');
module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowserOptions}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const extensionPath = path.join(__dirname, 'assets', 'simple-extension');
describeChromeOnly('headful tests', function() {
/* These tests fire up an actual browser so let's
* allow a higher timeout
*/
this.timeout(10 * 1000);
let headfulOptions;
let headlessOptions;
let extensionOptions;
beforeEach(() => {
const {defaultBrowserOptions} = getTestState();
headfulOptions = Object.assign({}, defaultBrowserOptions, {
headless: false
});
headlessOptions = Object.assign({}, defaultBrowserOptions, {
headless: true
});
extensionOptions = Object.assign({}, defaultBrowserOptions, {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
});
const headfulOptions = Object.assign({}, defaultBrowserOptions, {
headless: false
});
const headlessOptions = Object.assign({}, defaultBrowserOptions, {
headless: true
});
const extensionPath = path.join(__dirname, 'assets', 'simple-extension');
const extensionOptions = Object.assign({}, defaultBrowserOptions, {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
});
describe('HEADFUL', function() {
it('background_page target type should be available', async() => {
const {puppeteer} = getTestState();
const browserWithExtension = await puppeteer.launch(extensionOptions);
const page = await browserWithExtension.newPage();
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page');
@ -55,7 +69,8 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await browserWithExtension.close();
expect(backgroundPageTarget).toBeTruthy();
});
it('target.page() should return a background_page', async({}) => {
it('target.page() should return a background_page', async function() {
const {puppeteer} = getTestState();
const browserWithExtension = await puppeteer.launch(extensionOptions);
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page');
const page = await backgroundPageTarget.page();
@ -64,12 +79,15 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await browserWithExtension.close();
});
it('should have default url when launching browser', async function() {
const {puppeteer} = getTestState();
const browser = await puppeteer.launch(extensionOptions);
const pages = (await browser.pages()).map(page => page.url());
expect(pages).toEqual(['about:blank']);
await browser.close();
});
it('headless should be able to read cookies written by headful', async({server}) => {
it('headless should be able to read cookies written by headful', async() => {
const { server, puppeteer } = getTestState();
const userDataDir = await mkdtempAsync(TMP_FOLDER);
// Write a cookie in headful chrome
const headfulBrowser = await puppeteer.launch(Object.assign({userDataDir}, headfulOptions));
@ -88,7 +106,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
expect(cookie).toBe('foo=true');
});
// TODO: Support OOOPIF. @see https://github.com/puppeteer/puppeteer/issues/2548
xit('OOPIF: should report google.com frame', async({server}) => {
xit('OOPIF: should report google.com frame', async() => {
const { server } = getTestState();
// https://google.com is isolated by default in Chromium embedder.
const browser = await puppeteer.launch(headfulOptions);
const page = await browser.newPage();
@ -109,7 +129,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
]);
await browser.close();
});
it('should close browser with beforeunload page', async({server}) => {
it('should close browser with beforeunload page', async() => {
const { server, puppeteer } = getTestState();
const browser = await puppeteer.launch(headfulOptions);
const page = await browser.newPage();
await page.goto(server.PREFIX + '/beforeunload.html');
@ -118,7 +140,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await page.click('body');
await browser.close();
});
it('should open devtools when "devtools: true" option is given', async({server}) => {
it('should open devtools when "devtools: true" option is given', async() => {
const {puppeteer} = getTestState();
const browser = await puppeteer.launch(Object.assign({devtools: true}, headfulOptions));
const context = await browser.createIncognitoBrowserContext();
await Promise.all([
@ -131,6 +155,7 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
describe('Page.bringToFront', function() {
it('should work', async() => {
const {puppeteer} = getTestState();
const browser = await puppeteer.launch(headfulOptions);
const page1 = await browser.newPage();
const page2 = await browser.newPage();
@ -148,5 +173,6 @@ module.exports.addTests = function({testRunner, expect, puppeteer, defaultBrowse
await browser.close();
});
});
};
});

View File

@ -14,85 +14,107 @@
* limitations under the License.
*/
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe_fails_ffox('ignoreHTTPSErrors', function() {
beforeAll(async state => {
const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions);
state.browser = await puppeteer.launch(options);
});
afterAll(async state => {
await state.browser.close();
delete state.browser;
});
beforeEach(async state => {
state.context = await state.browser.createIncognitoBrowserContext();
state.page = await state.context.newPage();
});
afterEach(async state => {
await state.context.close();
delete state.context;
delete state.page;
});
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
describe('Response.securityDetails', function() {
it('should work', async({page, httpsServer}) => {
const [serverRequest, response] = await Promise.all([
httpsServer.waitForRequest('/empty.html'),
page.goto(httpsServer.EMPTY_PAGE)
]);
const securityDetails = response.securityDetails();
expect(securityDetails.issuer()).toBe('puppeteer-tests');
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
expect(securityDetails.protocol()).toBe(protocol);
expect(securityDetails.subjectName()).toBe('puppeteer-tests');
expect(securityDetails.validFrom()).toBe(1550084863);
expect(securityDetails.validTo()).toBe(33086084863);
});
it('should be |null| for non-secure requests', async({page, server}) => {
const response = await page.goto(server.EMPTY_PAGE);
expect(response.securityDetails()).toBe(null);
});
it('Network redirects should report SecurityDetails', async({page, httpsServer}) => {
httpsServer.setRedirect('/plzredirect', '/empty.html');
const responses = [];
page.on('response', response => responses.push(response));
const [serverRequest, ] = await Promise.all([
httpsServer.waitForRequest('/plzredirect'),
page.goto(httpsServer.PREFIX + '/plzredirect')
]);
expect(responses.length).toBe(2);
expect(responses[0].status()).toBe(302);
const securityDetails = responses[0].securityDetails();
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
expect(securityDetails.protocol()).toBe(protocol);
});
});
describeFailsFirefox('ignoreHTTPSErrors', function() {
/* Note that this test creates its own browser rather than use
* the one provided by the test set-up as we need one
* with ignoreHTTPSErrors set to true
*/
let browser;
let context;
let page;
it('should work', async({page, httpsServer}) => {
let error = null;
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
expect(error).toBe(null);
expect(response.ok()).toBe(true);
before(async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
const options = Object.assign({ignoreHTTPSErrors: true}, defaultBrowserOptions);
browser = await puppeteer.launch(options);
});
after(async() => {
await browser.close();
browser = null;
});
beforeEach(async() => {
context = await browser.createIncognitoBrowserContext();
page = await context.newPage();
});
afterEach(async() => {
await context.close();
context = null;
page = null;
});
describe('Response.securityDetails', function() {
it('should work', async() => {
const { httpsServer } = getTestState();
const [serverRequest, response] = await Promise.all([
httpsServer.waitForRequest('/empty.html'),
page.goto(httpsServer.EMPTY_PAGE)
]);
const securityDetails = response.securityDetails();
expect(securityDetails.issuer()).toBe('puppeteer-tests');
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
expect(securityDetails.protocol()).toBe(protocol);
expect(securityDetails.subjectName()).toBe('puppeteer-tests');
expect(securityDetails.validFrom()).toBe(1550084863);
expect(securityDetails.validTo()).toBe(33086084863);
});
it('should work with request interception', async({page, server, httpsServer}) => {
await page.setRequestInterception(true);
page.on('request', request => request.continue());
const response = await page.goto(httpsServer.EMPTY_PAGE);
expect(response.status()).toBe(200);
it('should be |null| for non-secure requests', async() => {
const { server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE);
expect(response.securityDetails()).toBe(null);
});
it('should work with mixed content', async({page, server, httpsServer}) => {
httpsServer.setRoute('/mixedcontent.html', (req, res) => {
res.end(`<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);
it('Network redirects should report SecurityDetails', async() => {
const { httpsServer } = getTestState();
httpsServer.setRedirect('/plzredirect', '/empty.html');
const responses = [];
page.on('response', response => responses.push(response));
const [serverRequest, ] = await Promise.all([
httpsServer.waitForRequest('/plzredirect'),
page.goto(httpsServer.PREFIX + '/plzredirect')
]);
expect(responses.length).toBe(2);
expect(responses[0].status()).toBe(302);
const securityDetails = responses[0].securityDetails();
const protocol = serverRequest.socket.getProtocol().replace('v', ' ');
expect(securityDetails.protocol()).toBe(protocol);
});
});
};
it('should work', async() => {
const { httpsServer } = getTestState();
let error = null;
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
expect(error).toBe(null);
expect(response.ok()).toBe(true);
});
it('should work with request interception', async() => {
const { httpsServer } = getTestState();
await page.setRequestInterception(true);
page.on('request', request => request.continue());
const response = await page.goto(httpsServer.EMPTY_PAGE);
expect(response.status()).toBe(200);
});
it('should work with mixed content', async() => {
const { server, httpsServer } = getTestState();
httpsServer.setRoute('/mixedcontent.html', (req, res) => {
res.end(`<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 expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
const FILE_TO_UPLOAD = path.join(__dirname, '/assets/file-to-upload.txt');
module.exports.addTests = function({testRunner, expect, puppeteer}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe_fails_ffox('input', function() {
it('should upload the file', async({page, server}) => {
describe('input tests', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describeFailsFirefox('input', function() {
it('should upload the file', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/fileupload.html');
const filePath = path.relative(process.cwd(), FILE_TO_UPLOAD);
const input = await page.$('input');
@ -44,8 +48,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
});
describe_fails_ffox('Page.waitForFileChooser', function() {
it('should work when file input is attached to DOM', async({page, server}) => {
describeFailsFirefox('Page.waitForFileChooser', function() {
it('should work when file input is attached to DOM', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([
page.waitForFileChooser(),
@ -53,7 +59,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]);
expect(chooser).toBeTruthy();
});
it('should work when file input is not attached to DOM', async({page, server}) => {
it('should work when file input is not attached to DOM', async() => {
const { page } = getTestState();
const [chooser] = await Promise.all([
page.waitForFileChooser(),
page.evaluate(() => {
@ -64,24 +72,32 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]);
expect(chooser).toBeTruthy();
});
it('should respect timeout', async({page, server}) => {
it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null;
await page.waitForFileChooser({timeout: 1}).catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should respect default timeout when there is no custom timeout', async({page, server}) => {
it('should respect default timeout when there is no custom timeout', async() => {
const { page, puppeteer } = getTestState();
page.setDefaultTimeout(1);
let error = null;
await page.waitForFileChooser().catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should prioritize exact timeout over default timeout', async({page, server}) => {
it('should prioritize exact timeout over default timeout', async() => {
const { page, puppeteer } = getTestState();
page.setDefaultTimeout(0);
let error = null;
await page.waitForFileChooser({timeout: 1}).catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should work with no timeout', async({page, server}) => {
it('should work with no timeout', async() => {
const { page } = getTestState();
const [chooser] = await Promise.all([
page.waitForFileChooser({timeout: 0}),
page.evaluate(() => setTimeout(() => {
@ -92,7 +108,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]);
expect(chooser).toBeTruthy();
});
it('should return the same file chooser when there are many watchdogs simultaneously', async({page, server}) => {
it('should return the same file chooser when there are many watchdogs simultaneously', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
const [fileChooser1, fileChooser2] = await Promise.all([
page.waitForFileChooser(),
@ -103,8 +121,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
});
describe_fails_ffox('FileChooser.accept', function() {
it('should accept single file', async({page, server}) => {
describeFailsFirefox('FileChooser.accept', function() {
it('should accept single file', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file oninput='javascript:console.timeStamp()'>`);
const [chooser] = await Promise.all([
page.waitForFileChooser(),
@ -117,7 +137,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await page.$eval('input', input => input.files.length)).toBe(1);
expect(await page.$eval('input', input => input.files[0].name)).toBe('file-to-upload.txt');
});
it('should be able to read selected file', async({page, server}) => {
it('should be able to read selected file', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD]));
expect(await page.$eval('input', async picker => {
@ -129,7 +151,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
return promise.then(() => reader.result);
})).toBe('contents of the file');
});
it('should be able to reset selected files with empty file list', async({page, server}) => {
it('should be able to reset selected files with empty file list', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
page.waitForFileChooser().then(chooser => chooser.accept([FILE_TO_UPLOAD]));
expect(await page.$eval('input', async picker => {
@ -144,7 +168,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
return picker.files.length;
})).toBe(0);
});
it('should not accept multiple files for single-file input', async({page, server}) => {
it('should not accept multiple files for single-file input', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([
page.waitForFileChooser(),
@ -157,7 +183,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]).catch(e => error = e);
expect(error).not.toBe(null);
});
it('should fail when accepting file chooser twice', async({page, server}) => {
it('should fail when accepting file chooser twice', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
@ -170,8 +198,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
});
describe_fails_ffox('FileChooser.cancel', function() {
it('should cancel dialog', async({page, server}) => {
describeFailsFirefox('FileChooser.cancel', function() {
it('should cancel dialog', async() => {
const { page } = getTestState();
// Consider file chooser canceled if we can summon another one.
// There's no reliable way in WebPlatform to see that FileChooser was
// canceled.
@ -187,7 +217,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
page.$eval('input', input => input.click()),
]);
});
it('should fail when canceling file chooser twice', async({page, server}) => {
it('should fail when canceling file chooser twice', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
@ -200,8 +232,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
});
});
describe_fails_ffox('FileChooser.isMultiple', () => {
it('should work for single file pick', async({page, server}) => {
describeFailsFirefox('FileChooser.isMultiple', () => {
it('should work for single file pick', async() => {
const { page } = getTestState();
await page.setContent(`<input type=file>`);
const [chooser] = await Promise.all([
page.waitForFileChooser(),
@ -209,7 +243,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]);
expect(chooser.isMultiple()).toBe(false);
});
it('should work for "multiple"', async({page, server}) => {
it('should work for "multiple"', async() => {
const { page } = getTestState();
await page.setContent(`<input multiple type=file>`);
const [chooser] = await Promise.all([
page.waitForFileChooser(),
@ -217,7 +253,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
]);
expect(chooser.isMultiple()).toBe(true);
});
it('should work for "webkitdirectory"', async({page, server}) => {
it('should work for "webkitdirectory"', async() => {
const { page } = getTestState();
await page.setContent(`<input multiple webkitdirectory type=file>`);
const [chooser] = await Promise.all([
page.waitForFileChooser(),
@ -226,4 +264,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(chooser.isMultiple()).toBe(true);
});
});
};
});

View File

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

View File

@ -16,239 +16,274 @@
const utils = require('./utils');
const os = require('os');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, FFOX}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Keyboard', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Keyboard', function() {
it('should type into a textarea', async({page, server}) => {
await page.evaluate(() => {
const textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.focus();
});
const text = 'Hello world. I am the text that was typed!';
await page.keyboard.type(text);
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text);
});
it_fails_ffox('should press the metaKey', async({page}) => {
await page.evaluate(() => {
window.keyPromise = new Promise(resolve => document.addEventListener('keydown', event => resolve(event.key)));
});
await page.keyboard.press('Meta');
expect(await page.evaluate('keyPromise')).toBe(FFOX && os.platform() !== 'darwin' ? 'OS' : 'Meta');
});
it('should move with the arrow keys', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.type('textarea', 'Hello World!');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
for (let i = 0; i < 'World!'.length; i++)
page.keyboard.press('ArrowLeft');
await page.keyboard.type('inserted ');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
page.keyboard.down('Shift');
for (let i = 0; i < 'inserted '.length; i++)
page.keyboard.press('ArrowLeft');
page.keyboard.up('Shift');
await page.keyboard.press('Backspace');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
});
it('should send a character with ElementHandle.press', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea');
await textarea.press('a');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
await textarea.press('b');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
});
it_fails_ffox('ElementHandle.press should support |text| option', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea');
await textarea.press('a', {text: 'ё'});
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('ё');
});
it_fails_ffox('should send a character with sendCharacter', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
await page.keyboard.sendCharacter('嗨');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨');
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
await page.keyboard.sendCharacter('a');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a');
});
it_fails_ffox('should report shiftKey', async({page, server}) => {
await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard;
const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17};
for (const modifierKey in codeForKey) {
await keyboard.down(modifierKey);
expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
await keyboard.down('!');
// Shift+! will generate a keypress
if (modifierKey === 'Shift')
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']');
else
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
await keyboard.up('!');
expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
await keyboard.up(modifierKey);
expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
}
});
it('should report multiple modifiers', async({page, server}) => {
await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard;
await keyboard.down('Control');
expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
await keyboard.down('Alt');
expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]');
await keyboard.down(';');
expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]');
await keyboard.up(';');
expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]');
await keyboard.up('Control');
expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]');
await keyboard.up('Alt');
expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []');
});
it('should send proper codes while typing', async({page, server}) => {
await page.goto(server.PREFIX + '/input/keyboard.html');
await page.keyboard.type('!');
expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: ! Digit1 49 []',
'Keypress: ! Digit1 33 33 []',
'Keyup: ! Digit1 49 []'].join('\n'));
await page.keyboard.type('^');
expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: ^ Digit6 54 []',
'Keypress: ^ Digit6 94 94 []',
'Keyup: ^ Digit6 54 []'].join('\n'));
});
it('should send proper codes while typing with shift', async({page, server}) => {
await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard;
await keyboard.down('Shift');
await page.keyboard.type('~');
expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: Shift ShiftLeft 16 [Shift]',
'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode
'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
await keyboard.up('Shift');
});
it('should not type canceled events', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
await page.evaluate(() => {
window.addEventListener('keydown', event => {
event.stopPropagation();
event.stopImmediatePropagation();
if (event.key === 'l')
event.preventDefault();
if (event.key === 'o')
event.preventDefault();
}, false);
});
await page.keyboard.type('Hello World!');
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
});
it_fails_ffox('should specify repeat property', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
await page.keyboard.down('a');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
await page.keyboard.press('a');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
await page.keyboard.down('b');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
await page.keyboard.down('b');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
await page.keyboard.up('a');
await page.keyboard.down('a');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
});
it_fails_ffox('should type all kinds of characters', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
const text = 'This text goes onto two lines.\nThis character is 嗨.';
await page.keyboard.type(text);
expect(await page.evaluate('result')).toBe(text);
});
it_fails_ffox('should specify location', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.evaluate(() => {
window.addEventListener('keydown', event => window.keyLocation = event.location, true);
});
const textarea = await page.$('textarea');
await textarea.press('Digit5');
expect(await page.evaluate('keyLocation')).toBe(0);
await textarea.press('ControlLeft');
expect(await page.evaluate('keyLocation')).toBe(1);
await textarea.press('ControlRight');
expect(await page.evaluate('keyLocation')).toBe(2);
await textarea.press('NumpadSubtract');
expect(await page.evaluate('keyLocation')).toBe(3);
});
it('should throw on unknown keys', async({page, server}) => {
let error = await page.keyboard.press('NotARealKey').catch(e => e);
expect(error.message).toBe('Unknown key: "NotARealKey"');
error = await page.keyboard.press('ё').catch(e => e);
expect(error && error.message).toBe('Unknown key: "ё"');
error = await page.keyboard.press('😊').catch(e => e);
expect(error && error.message).toBe('Unknown key: "😊"');
});
it_fails_ffox('should type emoji', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.type('textarea', '👹 Tokyo street Japan 🇯🇵');
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
});
it_fails_ffox('should type emoji into an iframe', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html');
const frame = page.frames()[1];
const textarea = await frame.$('textarea');
await textarea.type('👹 Tokyo street Japan 🇯🇵');
expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
});
it_fails_ffox('should press the meta key', async({page}) => {
await page.evaluate(() => {
window.result = null;
document.addEventListener('keydown', event => {
window.result = [event.key, event.code, event.metaKey];
});
});
await page.keyboard.press('Meta');
const [key, code, metaKey] = await page.evaluate('result');
if (FFOX && os.platform() !== 'darwin')
expect(key).toBe('OS');
else
expect(key).toBe('Meta');
if (FFOX)
expect(code).toBe('OSLeft');
else
expect(code).toBe('MetaLeft');
if (FFOX && os.platform() !== 'darwin')
expect(metaKey).toBe(false);
else
expect(metaKey).toBe(true);
it('should type into a textarea', async() => {
const { page } = getTestState();
await page.evaluate(() => {
const textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.focus();
});
const text = 'Hello world. I am the text that was typed!';
await page.keyboard.type(text);
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe(text);
});
};
itFailsFirefox('should press the metaKey', async() => {
const { page, isFirefox } = getTestState();
await page.evaluate(() => {
window.keyPromise = new Promise(resolve => document.addEventListener('keydown', event => resolve(event.key)));
});
await page.keyboard.press('Meta');
expect(await page.evaluate('keyPromise')).toBe(isFirefox && os.platform() !== 'darwin' ? 'OS' : 'Meta');
});
it('should move with the arrow keys', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.type('textarea', 'Hello World!');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
for (let i = 0; i < 'World!'.length; i++)
page.keyboard.press('ArrowLeft');
await page.keyboard.type('inserted ');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello inserted World!');
page.keyboard.down('Shift');
for (let i = 0; i < 'inserted '.length; i++)
page.keyboard.press('ArrowLeft');
page.keyboard.up('Shift');
await page.keyboard.press('Backspace');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('Hello World!');
});
it('should send a character with ElementHandle.press', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea');
await textarea.press('a');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
await textarea.press('b');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('a');
});
itFailsFirefox('ElementHandle.press should support |text| option', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea');
await textarea.press('a', {text: 'ё'});
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('ё');
});
itFailsFirefox('should send a character with sendCharacter', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
await page.keyboard.sendCharacter('嗨');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨');
await page.evaluate(() => window.addEventListener('keydown', e => e.preventDefault(), true));
await page.keyboard.sendCharacter('a');
expect(await page.evaluate(() => document.querySelector('textarea').value)).toBe('嗨a');
});
itFailsFirefox('should report shiftKey', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard;
const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17};
for (const modifierKey in codeForKey) {
await keyboard.down(modifierKey);
expect(await page.evaluate(() => getResult())).toBe('Keydown: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' [' + modifierKey + ']');
await keyboard.down('!');
// Shift+! will generate a keypress
if (modifierKey === 'Shift')
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']\nKeypress: ! Digit1 33 33 [' + modifierKey + ']');
else
expect(await page.evaluate(() => getResult())).toBe('Keydown: ! Digit1 49 [' + modifierKey + ']');
await keyboard.up('!');
expect(await page.evaluate(() => getResult())).toBe('Keyup: ! Digit1 49 [' + modifierKey + ']');
await keyboard.up(modifierKey);
expect(await page.evaluate(() => getResult())).toBe('Keyup: ' + modifierKey + ' ' + modifierKey + 'Left ' + codeForKey[modifierKey] + ' []');
}
});
it('should report multiple modifiers', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard;
await keyboard.down('Control');
expect(await page.evaluate(() => getResult())).toBe('Keydown: Control ControlLeft 17 [Control]');
await keyboard.down('Alt');
expect(await page.evaluate(() => getResult())).toBe('Keydown: Alt AltLeft 18 [Alt Control]');
await keyboard.down(';');
expect(await page.evaluate(() => getResult())).toBe('Keydown: ; Semicolon 186 [Alt Control]');
await keyboard.up(';');
expect(await page.evaluate(() => getResult())).toBe('Keyup: ; Semicolon 186 [Alt Control]');
await keyboard.up('Control');
expect(await page.evaluate(() => getResult())).toBe('Keyup: Control ControlLeft 17 [Alt]');
await keyboard.up('Alt');
expect(await page.evaluate(() => getResult())).toBe('Keyup: Alt AltLeft 18 []');
});
it('should send proper codes while typing', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/keyboard.html');
await page.keyboard.type('!');
expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: ! Digit1 49 []',
'Keypress: ! Digit1 33 33 []',
'Keyup: ! Digit1 49 []'].join('\n'));
await page.keyboard.type('^');
expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: ^ Digit6 54 []',
'Keypress: ^ Digit6 94 94 []',
'Keyup: ^ Digit6 54 []'].join('\n'));
});
it('should send proper codes while typing with shift', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard;
await keyboard.down('Shift');
await page.keyboard.type('~');
expect(await page.evaluate(() => getResult())).toBe(
[ 'Keydown: Shift ShiftLeft 16 [Shift]',
'Keydown: ~ Backquote 192 [Shift]', // 192 is ` keyCode
'Keypress: ~ Backquote 126 126 [Shift]', // 126 is ~ charCode
'Keyup: ~ Backquote 192 [Shift]'].join('\n'));
await keyboard.up('Shift');
});
it('should not type canceled events', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
await page.evaluate(() => {
window.addEventListener('keydown', event => {
event.stopPropagation();
event.stopImmediatePropagation();
if (event.key === 'l')
event.preventDefault();
if (event.key === 'o')
event.preventDefault();
}, false);
});
await page.keyboard.type('Hello World!');
expect(await page.evaluate(() => textarea.value)).toBe('He Wrd!');
});
itFailsFirefox('should specify repeat property', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
await page.evaluate(() => document.querySelector('textarea').addEventListener('keydown', e => window.lastEvent = e, true));
await page.keyboard.down('a');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
await page.keyboard.press('a');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
await page.keyboard.down('b');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
await page.keyboard.down('b');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(true);
await page.keyboard.up('a');
await page.keyboard.down('a');
expect(await page.evaluate(() => window.lastEvent.repeat)).toBe(false);
});
itFailsFirefox('should type all kinds of characters', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
const text = 'This text goes onto two lines.\nThis character is 嗨.';
await page.keyboard.type(text);
expect(await page.evaluate('result')).toBe(text);
});
itFailsFirefox('should specify location', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.evaluate(() => {
window.addEventListener('keydown', event => window.keyLocation = event.location, true);
});
const textarea = await page.$('textarea');
await textarea.press('Digit5');
expect(await page.evaluate('keyLocation')).toBe(0);
await textarea.press('ControlLeft');
expect(await page.evaluate('keyLocation')).toBe(1);
await textarea.press('ControlRight');
expect(await page.evaluate('keyLocation')).toBe(2);
await textarea.press('NumpadSubtract');
expect(await page.evaluate('keyLocation')).toBe(3);
});
it('should throw on unknown keys', async() => {
const { page } = getTestState();
let error = await page.keyboard.press('NotARealKey').catch(e => e);
expect(error.message).toBe('Unknown key: "NotARealKey"');
error = await page.keyboard.press('ё').catch(e => e);
expect(error && error.message).toBe('Unknown key: "ё"');
error = await page.keyboard.press('😊').catch(e => e);
expect(error && error.message).toBe('Unknown key: "😊"');
});
itFailsFirefox('should type emoji', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.type('textarea', '👹 Tokyo street Japan 🇯🇵');
expect(await page.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
});
itFailsFirefox('should type emoji into an iframe', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'emoji-test', server.PREFIX + '/input/textarea.html');
const frame = page.frames()[1];
const textarea = await frame.$('textarea');
await textarea.type('👹 Tokyo street Japan 🇯🇵');
expect(await frame.$eval('textarea', textarea => textarea.value)).toBe('👹 Tokyo street Japan 🇯🇵');
});
itFailsFirefox('should press the meta key', async() => {
const { page, isFirefox } = getTestState();
await page.evaluate(() => {
window.result = null;
document.addEventListener('keydown', event => {
window.result = [event.key, event.code, event.metaKey];
});
});
await page.keyboard.press('Meta');
const [key, code, metaKey] = await page.evaluate('result');
if (isFirefox && os.platform() !== 'darwin')
expect(key).toBe('OS');
else
expect(key).toBe('Meta');
if (isFirefox)
expect(code).toBe('OSLeft');
else
expect(code).toBe('MetaLeft');
if (isFirefox && os.platform() !== 'darwin')
expect(metaKey).toBe(false);
else
expect(metaKey).toBe(true);
});
});

View File

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

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.
*/
const os = require('os');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
function dimensions() {
const rect = document.querySelector('textarea').getBoundingClientRect();
@ -25,132 +27,144 @@ function dimensions() {
};
}
module.exports.addTests = function({testRunner, expect, FFOX}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Mouse', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should click the document', async() => {
const { page } = getTestState();
describe('Mouse', function() {
it('should click the document', async({page, server}) => {
await page.evaluate(() => {
window.clickPromise = new Promise(resolve => {
document.addEventListener('click', event => {
resolve({
type: event.type,
detail: event.detail,
clientX: event.clientX,
clientY: event.clientY,
isTrusted: event.isTrusted,
button: event.button
});
await page.evaluate(() => {
window.clickPromise = new Promise(resolve => {
document.addEventListener('click', event => {
resolve({
type: event.type,
detail: event.detail,
clientX: event.clientX,
clientY: event.clientY,
isTrusted: event.isTrusted,
button: event.button
});
});
});
await page.mouse.click(50, 60);
const event = await page.evaluate(() => window.clickPromise);
expect(event.type).toBe('click');
expect(event.detail).toBe(1);
expect(event.clientX).toBe(50);
expect(event.clientY).toBe(60);
expect(event.isTrusted).toBe(true);
expect(event.button).toBe(0);
});
it_fails_ffox('should resize the textarea', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
const {x, y, width, height} = await page.evaluate(dimensions);
const mouse = page.mouse;
await mouse.move(x + width - 4, y + height - 4);
await mouse.down();
await mouse.move(x + width + 100, y + height + 100);
await mouse.up();
const newDimensions = await page.evaluate(dimensions);
expect(newDimensions.width).toBe(Math.round(width + 104));
expect(newDimensions.height).toBe(Math.round(height + 104));
});
it_fails_ffox('should select the text with mouse', async({page, server}) => {
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
await page.keyboard.type(text);
// Firefox needs an extra frame here after typing or it will fail to set the scrollTop
await page.evaluate(() => new Promise(requestAnimationFrame));
await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
const {x, y} = await page.evaluate(dimensions);
await page.mouse.move(x + 2,y + 2);
await page.mouse.down();
await page.mouse.move(100,100);
await page.mouse.up();
expect(await page.evaluate(() => {
const textarea = document.querySelector('textarea');
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
})).toBe(text);
});
it_fails_ffox('should trigger hover state', async({page, server}) => {
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.hover('#button-6');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
await page.hover('#button-2');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2');
await page.hover('#button-91');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
});
it_fails_ffox('should trigger hover state with removed window.Node', async({page, server}) => {
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.evaluate(() => delete window.Node);
await page.hover('#button-6');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
});
it('should set modifier keys on click', async({page, server}) => {
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true));
const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'};
// In Firefox, the Meta modifier only exists on Mac
if (FFOX && os.platform() !== 'darwin')
delete modifiers['Meta'];
for (const modifier in modifiers) {
await page.keyboard.down(modifier);
await page.click('#button-3');
if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
throw new Error(modifiers[modifier] + ' should be true');
await page.keyboard.up(modifier);
}
await page.click('#button-3');
for (const modifier in modifiers) {
if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
throw new Error(modifiers[modifier] + ' should be false');
}
});
it_fails_ffox('should tween mouse movement', async({page, server}) => {
await page.mouse.move(100, 100);
await page.evaluate(() => {
window.result = [];
document.addEventListener('mousemove', event => {
window.result.push([event.clientX, event.clientY]);
});
});
await page.mouse.move(200, 300, {steps: 5});
expect(await page.evaluate('result')).toEqual([
[120, 140],
[140, 180],
[160, 220],
[180, 260],
[200, 300]
]);
});
// @see https://crbug.com/929806
it_fails_ffox('should work with mobile viewports and cross process navigations', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.setViewport({width: 360, height: 640, isMobile: true});
await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
await page.evaluate(() => {
document.addEventListener('click', event => {
window.result = {x: event.clientX, y: event.clientY};
});
});
await page.mouse.click(30, 40);
expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
});
await page.mouse.click(50, 60);
const event = await page.evaluate(() => window.clickPromise);
expect(event.type).toBe('click');
expect(event.detail).toBe(1);
expect(event.clientX).toBe(50);
expect(event.clientY).toBe(60);
expect(event.isTrusted).toBe(true);
expect(event.button).toBe(0);
});
};
itFailsFirefox('should resize the textarea', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
const {x, y, width, height} = await page.evaluate(dimensions);
const mouse = page.mouse;
await mouse.move(x + width - 4, y + height - 4);
await mouse.down();
await mouse.move(x + width + 100, y + height + 100);
await mouse.up();
const newDimensions = await page.evaluate(dimensions);
expect(newDimensions.width).toBe(Math.round(width + 104));
expect(newDimensions.height).toBe(Math.round(height + 104));
});
itFailsFirefox('should select the text with mouse', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/textarea.html');
await page.focus('textarea');
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
await page.keyboard.type(text);
// Firefox needs an extra frame here after typing or it will fail to set the scrollTop
await page.evaluate(() => new Promise(requestAnimationFrame));
await page.evaluate(() => document.querySelector('textarea').scrollTop = 0);
const {x, y} = await page.evaluate(dimensions);
await page.mouse.move(x + 2,y + 2);
await page.mouse.down();
await page.mouse.move(100,100);
await page.mouse.up();
expect(await page.evaluate(() => {
const textarea = document.querySelector('textarea');
return textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
})).toBe(text);
});
itFailsFirefox('should trigger hover state', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.hover('#button-6');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
await page.hover('#button-2');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-2');
await page.hover('#button-91');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-91');
});
itFailsFirefox('should trigger hover state with removed window.Node', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.evaluate(() => delete window.Node);
await page.hover('#button-6');
expect(await page.evaluate(() => document.querySelector('button:hover').id)).toBe('button-6');
});
it('should set modifier keys on click', async() => {
const { page, server, isFirefox } = getTestState();
await page.goto(server.PREFIX + '/input/scrollable.html');
await page.evaluate(() => document.querySelector('#button-3').addEventListener('mousedown', e => window.lastEvent = e, true));
const modifiers = {'Shift': 'shiftKey', 'Control': 'ctrlKey', 'Alt': 'altKey', 'Meta': 'metaKey'};
// In Firefox, the Meta modifier only exists on Mac
if (isFirefox && os.platform() !== 'darwin')
delete modifiers['Meta'];
for (const modifier in modifiers) {
await page.keyboard.down(modifier);
await page.click('#button-3');
if (!(await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
throw new Error(modifiers[modifier] + ' should be true');
await page.keyboard.up(modifier);
}
await page.click('#button-3');
for (const modifier in modifiers) {
if ((await page.evaluate(mod => window.lastEvent[mod], modifiers[modifier])))
throw new Error(modifiers[modifier] + ' should be false');
}
});
itFailsFirefox('should tween mouse movement', async() => {
const { page } = getTestState();
await page.mouse.move(100, 100);
await page.evaluate(() => {
window.result = [];
document.addEventListener('mousemove', event => {
window.result.push([event.clientX, event.clientY]);
});
});
await page.mouse.move(200, 300, {steps: 5});
expect(await page.evaluate('result')).toEqual([
[120, 140],
[140, 180],
[160, 220],
[180, 260],
[200, 300]
]);
});
// @see https://crbug.com/929806
itFailsFirefox('should work with mobile viewports and cross process navigations', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setViewport({width: 360, height: 640, isMobile: true});
await page.goto(server.CROSS_PROCESS_PREFIX + '/mobile.html');
await page.evaluate(() => {
document.addEventListener('click', event => {
window.result = {x: event.clientX, y: event.clientY};
});
});
await page.mouse.click(30, 40);
expect(await page.evaluate('result')).toEqual({x: 30, y: 40});
});
});

View File

@ -15,18 +15,22 @@
*/
const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('navigation', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Page.goto', function() {
it('should work', async({page, server}) => {
it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
expect(page.url()).toBe(server.EMPTY_PAGE);
});
it_fails_ffox('should work with anchor navigation', async({page, server}) => {
itFailsFirefox('should work with anchor navigation', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
expect(page.url()).toBe(server.EMPTY_PAGE);
await page.goto(server.EMPTY_PAGE + '#foo');
@ -34,28 +38,38 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
await page.goto(server.EMPTY_PAGE + '#bar');
expect(page.url()).toBe(server.EMPTY_PAGE + '#bar');
});
it('should work with redirects', async({page, server}) => {
it('should work with redirects', async() => {
const { page, server } = getTestState();
server.setRedirect('/redirect/1.html', '/redirect/2.html');
server.setRedirect('/redirect/2.html', '/empty.html');
await page.goto(server.PREFIX + '/redirect/1.html');
expect(page.url()).toBe(server.EMPTY_PAGE);
});
it('should navigate to about:blank', async({page, server}) => {
it('should navigate to about:blank', async() => {
const { page } = getTestState();
const response = await page.goto('about:blank');
expect(response).toBe(null);
});
it_fails_ffox('should return response when page changes its URL after load', async({page, server}) => {
itFailsFirefox('should return response when page changes its URL after load', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.PREFIX + '/historyapi.html');
expect(response.status()).toBe(200);
});
it('should work with subframes return 204', async({page, server}) => {
it('should work with subframes return 204', async() => {
const { page, server } = getTestState();
server.setRoute('/frames/frame.html', (req, res) => {
res.statusCode = 204;
res.end();
});
await page.goto(server.PREFIX + '/frames/one-frame.html');
});
it_fails_ffox('should fail when server returns 204', async({page, server}) => {
itFailsFirefox('should fail when server returns 204', async() => {
const { page, server, isChrome } = getTestState();
server.setRoute('/empty.html', (req, res) => {
res.statusCode = 204;
res.end();
@ -63,16 +77,20 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).not.toBe(null);
if (CHROME)
if (isChrome)
expect(error.message).toContain('net::ERR_ABORTED');
else
expect(error.message).toContain('NS_BINDING_ABORTED');
});
it_fails_ffox('should navigate to empty page with domcontentloaded', async({page, server}) => {
itFailsFirefox('should navigate to empty page with domcontentloaded', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
expect(response.status()).toBe(200);
});
it_fails_ffox('should work when page calls history API in beforeunload', async({page, server}) => {
itFailsFirefox('should work when page calls history API in beforeunload', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false);
@ -80,23 +98,31 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
const response = await page.goto(server.PREFIX + '/grid.html');
expect(response.status()).toBe(200);
});
it_fails_ffox('should navigate to empty page with networkidle0', async({page, server}) => {
itFailsFirefox('should navigate to empty page with networkidle0', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle0'});
expect(response.status()).toBe(200);
});
it_fails_ffox('should navigate to empty page with networkidle2', async({page, server}) => {
itFailsFirefox('should navigate to empty page with networkidle2', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle2'});
expect(response.status()).toBe(200);
});
it_fails_ffox('should fail when navigating to bad url', async({page, server}) => {
itFailsFirefox('should fail when navigating to bad url', async() => {
const { page, isChrome } = getTestState();
let error = null;
await page.goto('asdfasdf').catch(e => error = e);
if (CHROME)
if (isChrome)
expect(error.message).toContain('Cannot navigate to invalid URL');
else
expect(error.message).toContain('Invalid url');
});
it_fails_ffox('should fail when navigating to bad SSL', async({page, httpsServer}) => {
itFailsFirefox('should fail when navigating to bad SSL', async() => {
const { page, httpsServer, isChrome } = getTestState();
// Make sure that network events do not emit 'undefined'.
// @see https://crbug.com/750469
page.on('request', request => expect(request).toBeTruthy());
@ -104,35 +130,43 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
page.on('requestfailed', request => expect(request).toBeTruthy());
let error = null;
await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
if (CHROME)
if (isChrome)
expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID');
else
expect(error.message).toContain('SSL_ERROR_UNKNOWN');
});
it_fails_ffox('should fail when navigating to bad SSL after redirects', async({page, server, httpsServer}) => {
itFailsFirefox('should fail when navigating to bad SSL after redirects', async() => {
const { page, server, httpsServer, isChrome } = getTestState();
server.setRedirect('/redirect/1.html', '/redirect/2.html');
server.setRedirect('/redirect/2.html', '/empty.html');
let error = null;
await page.goto(httpsServer.PREFIX + '/redirect/1.html').catch(e => error = e);
if (CHROME)
if (isChrome)
expect(error.message).toContain('net::ERR_CERT_AUTHORITY_INVALID');
else
expect(error.message).toContain('SSL_ERROR_UNKNOWN');
});
it('should throw if networkidle is passed as an option', async({page, server}) => {
it('should throw if networkidle is passed as an option', async() => {
const { page, server } = getTestState();
let error = null;
await page.goto(server.EMPTY_PAGE, {waitUntil: 'networkidle'}).catch(err => error = err);
expect(error.message).toContain('"networkidle" option is no longer supported');
});
it_fails_ffox('should fail when main resources failed to load', async({page, server}) => {
itFailsFirefox('should fail when main resources failed to load', async() => {
const { page, isChrome } = getTestState();
let error = null;
await page.goto('http://localhost:44123/non-existing-url').catch(e => error = e);
if (CHROME)
if (isChrome)
expect(error.message).toContain('net::ERR_CONNECTION_REFUSED');
else
expect(error.message).toContain('NS_ERROR_CONNECTION_REFUSED');
});
it('should fail when exceeding maximum navigation timeout', async({page, server}) => {
it('should fail when exceeding maximum navigation timeout', async() => {
const { page, server, puppeteer } = getTestState();
// Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { });
let error = null;
@ -140,7 +174,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(error.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should fail when exceeding default maximum navigation timeout', async({page, server}) => {
it('should fail when exceeding default maximum navigation timeout', async() => {
const { page, server, puppeteer } = getTestState();
// Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { });
let error = null;
@ -149,7 +185,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(error.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should fail when exceeding default maximum timeout', async({page, server}) => {
it('should fail when exceeding default maximum timeout', async() => {
const { page, server, puppeteer } = getTestState();
// Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { });
let error = null;
@ -158,7 +196,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(error.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should prioritize default navigation timeout over default timeout', async({page, server}) => {
it('should prioritize default navigation timeout over default timeout', async() => {
const { page, server, puppeteer } = getTestState();
// Hang for request to the empty.html
server.setRoute('/empty.html', (req, res) => { });
let error = null;
@ -168,7 +208,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(error.message).toContain('Navigation timeout of 1 ms exceeded');
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should disable timeout when its set to 0', async({page, server}) => {
it('should disable timeout when its set to 0', async() => {
const { page, server } = getTestState();
let error = null;
let loaded = false;
page.once('load', () => loaded = true);
@ -176,20 +218,28 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(error).toBe(null);
expect(loaded).toBe(true);
});
it_fails_ffox('should work when navigating to valid url', async({page, server}) => {
itFailsFirefox('should work when navigating to valid url', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.EMPTY_PAGE);
expect(response.ok()).toBe(true);
});
it_fails_ffox('should work when navigating to data url', async({page, server}) => {
itFailsFirefox('should work when navigating to data url', async() => {
const { page } = getTestState();
const response = await page.goto('data:text/html,hello');
expect(response.ok()).toBe(true);
});
it_fails_ffox('should work when navigating to 404', async({page, server}) => {
itFailsFirefox('should work when navigating to 404', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.PREFIX + '/not-found');
expect(response.ok()).toBe(false);
expect(response.status()).toBe(404);
});
it_fails_ffox('should return last response in redirect chain', async({page, server}) => {
itFailsFirefox('should return last response in redirect chain', async() => {
const { page, server } = getTestState();
server.setRedirect('/redirect/1.html', '/redirect/2.html');
server.setRedirect('/redirect/2.html', '/redirect/3.html');
server.setRedirect('/redirect/3.html', server.EMPTY_PAGE);
@ -197,7 +247,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response.ok()).toBe(true);
expect(response.url()).toBe(server.EMPTY_PAGE);
});
it_fails_ffox('should wait for network idle to succeed navigation', async({page, server}) => {
itFailsFirefox('should wait for network idle to succeed navigation', async() => {
const { page, server } = getTestState();
let responses = [];
// Hold on to a bunch of requests without answering.
server.setRoute('/fetch-request-a.js', (req, res) => responses.push(res));
@ -254,7 +306,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
// Expect navigation to succeed.
expect(response.ok()).toBe(true);
});
it('should not leak listeners during navigation', async({page, server}) => {
it('should not leak listeners during navigation', async() => {
const { page, server } = getTestState();
let warning = null;
const warningHandler = w => warning = w;
process.on('warning', warningHandler);
@ -263,7 +317,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
process.removeListener('warning', warningHandler);
expect(warning).toBe(null);
});
it_fails_ffox('should not leak listeners during bad navigation', async({page, server}) => {
itFailsFirefox('should not leak listeners during bad navigation', async() => {
const { page } = getTestState();
let warning = null;
const warningHandler = w => warning = w;
process.on('warning', warningHandler);
@ -272,7 +328,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
process.removeListener('warning', warningHandler);
expect(warning).toBe(null);
});
it('should not leak listeners during navigation of 11 pages', async({page, context, server}) => {
it('should not leak listeners during navigation of 11 pages', async() => {
const { context, server } = getTestState();
let warning = null;
const warningHandler = w => warning = w;
process.on('warning', warningHandler);
@ -284,7 +342,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
process.removeListener('warning', warningHandler);
expect(warning).toBe(null);
});
it_fails_ffox('should navigate to dataURL and fire dataURL requests', async({page, server}) => {
itFailsFirefox('should navigate to dataURL and fire dataURL requests', async() => {
const { page } = getTestState();
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
const dataURL = 'data:text/html,<div>yo</div>';
@ -293,7 +353,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(dataURL);
});
it_fails_ffox('should navigate to URL with hash and fire requests without hash', async({page, server}) => {
itFailsFirefox('should navigate to URL with hash and fire requests without hash', async() => {
const { page, server } = getTestState();
const requests = [];
page.on('request', request => !utils.isFavicon(request) && requests.push(request));
const response = await page.goto(server.EMPTY_PAGE + '#hash');
@ -302,12 +364,16 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
});
it_fails_ffox('should work with self requesting page', async({page, server}) => {
itFailsFirefox('should work with self requesting page', async() => {
const { page, server } = getTestState();
const response = await page.goto(server.PREFIX + '/self-request.html');
expect(response.status()).toBe(200);
expect(response.url()).toContain('self-request.html');
});
it_fails_ffox('should fail when navigating and show the url at the error message', async function({page, server, httpsServer}) {
itFailsFirefox('should fail when navigating and show the url at the error message', async() => {
const { page, httpsServer } = getTestState();
const url = httpsServer.PREFIX + '/redirect/1.html';
let error = null;
try {
@ -317,7 +383,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
}
expect(error.message).toContain(url);
});
it_fails_ffox('should send referer', async({page, server}) => {
itFailsFirefox('should send referer', async() => {
const { page, server } = getTestState();
const [request1, request2] = await Promise.all([
server.waitForRequest('/grid.html'),
server.waitForRequest('/digits/1.png'),
@ -332,7 +400,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
});
describe('Page.waitForNavigation', function() {
it_fails_ffox('should work', async({page, server}) => {
itFailsFirefox('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
const [response] = await Promise.all([
page.waitForNavigation(),
@ -341,7 +411,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response.ok()).toBe(true);
expect(response.url()).toContain('grid.html');
});
it('should work with both domcontentloaded and load', async({page, server}) => {
it('should work with both domcontentloaded and load', async() => {
const { page, server } = getTestState();
let response = null;
server.setRoute('/one-style.css', (req, res) => response = res);
const navigationPromise = page.goto(server.PREFIX + '/one-style.html');
@ -361,7 +433,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
await bothFiredPromise;
await navigationPromise;
});
it_fails_ffox('should work with clicking on anchor links', async({page, server}) => {
itFailsFirefox('should work with clicking on anchor links', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setContent(`<a href='#foobar'>foobar</a>`);
const [response] = await Promise.all([
@ -371,7 +445,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response).toBe(null);
expect(page.url()).toBe(server.EMPTY_PAGE + '#foobar');
});
it_fails_ffox('should work with history.pushState()', async({page, server}) => {
itFailsFirefox('should work with history.pushState()', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setContent(`
<a onclick='javascript:pushState()'>SPA</a>
@ -386,7 +462,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/wow.html');
});
it_fails_ffox('should work with history.replaceState()', async({page, server}) => {
itFailsFirefox('should work with history.replaceState()', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setContent(`
<a onclick='javascript:replaceState()'>SPA</a>
@ -401,7 +479,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response).toBe(null);
expect(page.url()).toBe(server.PREFIX + '/replaced.html');
});
it_fails_ffox('should work with DOM history.back()/history.forward()', async({page, server}) => {
itFailsFirefox('should work with DOM history.back()/history.forward()', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setContent(`
<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(page.url()).toBe(server.PREFIX + '/second.html');
});
it_fails_ffox('should work when subframe issues window.stop()', async({page, server}) => {
itFailsFirefox('should work when subframe issues window.stop()', async() => {
const { page, server } = getTestState();
server.setRoute('/frames/style.css', (req, res) => {});
const navigationPromise = page.goto(server.PREFIX + '/frames/one-frame.html');
const frame = await utils.waitEvent(page, 'frameattached');
@ -444,8 +526,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
});
});
describe_fails_ffox('Page.goBack', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('Page.goBack', function() {
it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.goto(server.PREFIX + '/grid.html');
@ -460,7 +544,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
response = await page.goForward();
expect(response).toBe(null);
});
it('should work with HistoryAPI', async({page, server}) => {
it('should work with HistoryAPI', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => {
history.pushState({}, '', '/first.html');
@ -477,8 +563,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
});
});
describe_fails_ffox('Frame.goto', function() {
it('should navigate subframes', async({page, server}) => {
describeFailsFirefox('Frame.goto', function() {
it('should navigate subframes', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html');
expect(page.frames()[0].url()).toContain('/frames/one-frame.html');
expect(page.frames()[1].url()).toContain('/frames/frame.html');
@ -487,7 +575,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response.ok()).toBe(true);
expect(response.frame()).toBe(page.frames()[1]);
});
it('should reject when frame detaches', async({page, server}) => {
it('should reject when frame detaches', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html');
server.setRoute('/empty.html', () => {});
@ -498,7 +588,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
const error = await navigationPromise;
expect(error.message).toBe('Navigating frame was detached');
});
it('should return matching responses', async({page, server}) => {
it('should return matching responses', async() => {
const { page, server } = getTestState();
// Disable cache: otherwise, chromium will cache similar requests.
await page.setCacheEnabled(false);
await page.goto(server.EMPTY_PAGE);
@ -527,8 +619,10 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
});
});
describe_fails_ffox('Frame.waitForNavigation', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('Frame.waitForNavigation', function() {
it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html');
const frame = page.frames()[1];
const [response] = await Promise.all([
@ -540,7 +634,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
expect(response.frame()).toBe(frame);
expect(page.url()).toContain('/frames/one-frame.html');
});
it('should fail when frame detaches', async({page, server}) => {
it('should fail when frame detaches', async() => {
const { page, server } = getTestState();
await page.goto(server.PREFIX + '/frames/one-frame.html');
const frame = page.frames()[1];
@ -558,11 +654,13 @@ module.exports.addTests = function({testRunner, expect, puppeteer, CHROME}) {
});
describe('Page.reload', function() {
it('should work', async({page, server}) => {
it('should work', async() => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => window._foo = 10);
await page.reload();
expect(await page.evaluate(() => window._foo)).toBe(undefined);
});
});
};
});

View File

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

View File

@ -14,43 +14,53 @@
* limitations under the License.
*/
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
describe('OOPIF', function() {
beforeAll(async function(state) {
state.browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: (defaultBrowserOptions.args || []).concat(['--site-per-process']),
}));
});
beforeEach(async function(state) {
state.context = await state.browser.createIncognitoBrowserContext();
state.page = await state.context.newPage();
});
afterEach(async function(state) {
await state.context.close();
state.page = null;
state.context = null;
});
afterAll(async function(state) {
await state.browser.close();
state.browser = null;
});
xit('should report oopif frames', async function({page, server, context}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(context).length).toBe(1);
expect(page.frames().length).toBe(2);
});
it('should load oopif iframes with subresources and request interception', async function({page, server, context}) {
await page.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(context).length).toBe(1);
});
describeChromeOnly('OOPIF', function() {
/* We use a special browser for this test as we need the --site-per-process flag */
let browser;
let context;
let page;
before(async() => {
const {puppeteer, defaultBrowserOptions} = getTestState();
browser = await puppeteer.launch(Object.assign({}, defaultBrowserOptions, {
args: (defaultBrowserOptions.args || []).concat(['--site-per-process']),
}));
});
};
beforeEach(async() => {
context = await browser.createIncognitoBrowserContext();
page = await context.newPage();
});
afterEach(async() => {
await context.close();
page = null;
context = null;
});
after(async() => {
await browser.close();
browser = null;
});
xit('should report oopif frames', async() => {
const { server } = getTestState();
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(context).length).toBe(1);
expect(page.frames().length).toBe(2);
});
it('should load oopif iframes with subresources and request interception', async() => {
const { server } = getTestState();
await page.setRequestInterception(true);
page.on('request', request => request.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(oopifs(context).length).toBe(1);
});
});
/**

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
* limitations under the License.
*/
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, product}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('querySelector', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
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>');
const idAttribute = await page.$eval('section', e => e.id);
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>');
const text = await page.$eval('section', (e, suffix) => e.textContent + suffix, ' 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>');
const divHandle = await page.$('div');
const text = await page.$eval('section', (e, div) => e.textContent + div.textContent, divHandle);
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;
await page.$eval('section', e => e.id).catch(e => error = e);
expect(error.message).toContain('failed to find element matching selector "section"');
});
});
describe_fails_ffox('Page.$$eval', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('Page.$$eval', function() {
it('should work', async() => {
const { page } = getTestState();
await page.setContent('<div>hello</div><div>beautiful</div><div>world!</div>');
const divsCount = await page.$$eval('div', divs => divs.length);
expect(divsCount).toBe(3);
});
});
describe_fails_ffox('Page.$', function() {
it('should query existing element', async({page, server}) => {
describeFailsFirefox('Page.$', function() {
it('should query existing element', async() => {
const { page } = getTestState();
await page.setContent('<section>test</section>');
const element = await page.$('section');
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');
expect(element).toBe(null);
});
});
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>');
const elements = await page.$$('div');
expect(elements.length).toBe(2);
const promises = elements.map(element => page.evaluate(e => e.textContent, element));
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);
const elements = await page.$$('div');
expect(elements.length).toBe(0);
});
});
describe_fails_ffox('Path.$x', function() {
it('should query existing element', async({page, server}) => {
describeFailsFirefox('Path.$x', function() {
it('should query existing element', async() => {
const { page } = getTestState();
await page.setContent('<section>test</section>');
const elements = await page.$x('/html/body/section');
expect(elements[0]).toBeTruthy();
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');
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>');
const elements = await page.$x('/html/body/div');
expect(elements.length).toBe(2);
@ -98,7 +122,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
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.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>');
const html = await page.$('html');
@ -108,22 +134,28 @@ module.exports.addTests = function({testRunner, expect, product}) {
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>');
const html = await page.$('html');
const second = await html.$('.third');
expect(second).toBe(null);
});
});
describe_fails_ffox('ElementHandle.$eval', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('ElementHandle.$eval', function() {
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>');
const tweet = await page.$('.tweet');
const content = await tweet.$eval('.like', node => node.innerText);
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>';
await page.setContent(htmlContent);
const elementHandle = await page.$('#myId');
@ -131,7 +163,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
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>';
await page.setContent(htmlContent);
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"`);
});
});
describe_fails_ffox('ElementHandle.$$eval', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('ElementHandle.$$eval', function() {
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>');
const tweet = await page.$('.tweet');
const content = await tweet.$$eval('.like', nodes => nodes.map(n => n.innerText));
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>';
await page.setContent(htmlContent);
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']);
});
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>';
await page.setContent(htmlContent);
const elementHandle = await page.$('#myId');
@ -165,8 +205,10 @@ module.exports.addTests = function({testRunner, expect, product}) {
});
describe_fails_ffox('ElementHandle.$$', function() {
it('should query existing elements', async({page, server}) => {
describeFailsFirefox('ElementHandle.$$', function() {
it('should query existing elements', async() => {
const { page } = getTestState();
await page.setContent('<html><body><div>A</div><br/><div>B</div></body></html>');
const html = await page.$('html');
const elements = await html.$$('div');
@ -175,7 +217,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
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>');
const html = await page.$('html');
const elements = await html.$$('div');
@ -185,7 +229,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
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.setContent('<html><body><div class="second"><div class="inner">A</div></div></body></html>');
const html = await page.$('html');
@ -195,11 +241,13 @@ module.exports.addTests = function({testRunner, expect, product}) {
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>');
const html = await page.$('html');
const second = await html.$x(`/div[contains(@class, 'third')]`);
expect(second).toEqual([]);
});
});
};
});

View File

@ -17,14 +17,16 @@
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, CHROME}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('request interception', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
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);
page.on('request', request => {
if (utils.isFavicon(request)) {
@ -45,7 +47,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.ok()).toBe(true);
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');
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(true);
@ -61,7 +65,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]);
});
// @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');
await page.setRequestInterception(true);
page.on('request', request => {
@ -73,7 +79,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await page.goto(server.PREFIX + '/rrredirect');
});
// @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);
page.on('request', request => {
const headers = Object.assign({}, request.headers(), {
@ -90,7 +98,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
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);
const requests = [];
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].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.
await page.goto(server.EMPTY_PAGE);
await page.setCookie({ name: 'foo', value: 'bar'});
@ -113,14 +125,18 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const response = await page.reload();
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);
page.once('request', request => request.continue());
await page.goto(server.EMPTY_PAGE);
await page.setRequestInterception(false);
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({
foo: 'bar'
});
@ -133,7 +149,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.ok()).toBe(true);
});
// @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);
server.setRedirect('/logo.png', '/pptr.png');
await page.setRequestInterception(true);
@ -146,7 +164,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
});
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.setRequestInterception(true);
page.on('request', request => {
@ -156,7 +176,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
const response = await page.goto(server.EMPTY_PAGE);
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);
page.on('request', request => {
if (request.url().endsWith('.css'))
@ -171,7 +193,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.request().failure()).toBe(null);
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);
page.on('request', request => {
request.abort('internetdisconnected');
@ -182,7 +206,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(failedRequest).toBeTruthy();
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({
referer: 'http://google.com/'
});
@ -194,18 +220,22 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]);
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);
page.on('request', request => request.abort());
let error = null;
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
expect(error).toBeTruthy();
if (CHROME)
if (isChrome)
expect(error.message).toContain('net::ERR_FAILED');
else
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);
const requests = [];
page.on('request', request => {
@ -232,7 +262,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
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);
const requests = [];
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[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);
server.setRedirect('/non-existing.json', '/non-existing-2.json');
server.setRedirect('/non-existing-2.json', '/simple.html');
@ -275,12 +309,14 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
return e.message;
}
});
if (CHROME)
if (isChrome)
expect(result).toContain('Failed to fetch');
else
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);
let responseCount = 1;
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']);
});
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);
const requests = [];
page.on('request', request => {
@ -316,7 +354,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(1);
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.setRequestInterception(true);
const requests = [];
@ -330,7 +370,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(1);
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);
const requests = [];
page.on('request', request => {
@ -343,7 +385,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(1);
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
// report URL as-is. @see crbug.com/759388
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');
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);
server.setRoute('/malformed?rnd=%911', (req, res) => res.end());
page.on('request', request => request.continue());
const response = await page.goto(server.PREFIX + '/malformed?rnd=%911');
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
// report encoded URL for stylesheet. @see crbug.com/759388
await page.setRequestInterception(true);
@ -372,7 +420,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(requests.length).toBe(2);
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.setRequestInterception(true);
let request = null;
@ -386,7 +436,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await request.continue().catch(e => error = e);
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;
page.on('request', async request => {
try {
@ -398,7 +450,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
await page.goto(server.EMPTY_PAGE);
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);
const urls = new Set();
page.on('request', request => {
@ -412,13 +466,17 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
});
});
describe_fails_ffox('Request.continue', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('Request.continue', function() {
it('should work', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true);
page.on('request', request => request.continue());
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);
page.on('request', request => {
const headers = Object.assign({}, request.headers());
@ -432,7 +490,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]);
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);
page.on('request', request => {
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(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.setRequestInterception(true);
@ -457,7 +519,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]);
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.setRequestInterception(true);
@ -470,7 +534,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
]);
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);
page.on('request', request => {
request.continue({ method: 'POST', postData: 'doggo' });
@ -484,8 +550,10 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
});
});
describe_fails_ffox('Request.respond', function() {
it('should work', async({page, server}) => {
describeFailsFirefox('Request.respond', function() {
it('should work', async() => {
const { page, server } = getTestState();
await page.setRequestInterception(true);
page.on('request', request => {
request.respond({
@ -501,7 +569,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.headers().foo).toBe('bar');
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);
page.on('request', request => {
request.respond({
@ -514,7 +584,9 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(response.statusText()).toBe('Unprocessable Entity');
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);
page.on('request', request => {
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.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);
page.on('request', request => {
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');
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);
page.on('request', request => {
request.respond({
@ -569,8 +645,7 @@ module.exports.addTests = function({testRunner, expect, CHROME}) {
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!');
});
});
};
});
/**
* @param {string} path

View File

@ -14,19 +14,25 @@
* limitations under the License.
*/
module.exports.addTests = function({testRunner, expect, product}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
describe('Screenshots', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
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.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot();
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.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
@ -39,7 +45,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
});
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.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
@ -52,7 +60,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
});
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.goto(server.PREFIX + '/grid.html');
const promises = [];
@ -69,7 +79,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshots = await Promise.all(promises);
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.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
@ -77,7 +89,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
});
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 pages = await Promise.all(Array(N).fill(0).map(async() => {
const page = await context.newPage();
@ -92,19 +106,25 @@ module.exports.addTests = function({testRunner, expect, product}) {
expect(screenshots[i]).toBeGolden(`grid-cell-${i}.png`);
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.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true});
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.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
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({
clip: {
x: 0,
@ -115,7 +135,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
});
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.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
@ -126,7 +148,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
});
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.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(50, 100));
@ -134,7 +158,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot();
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.setContent(`
something above
@ -151,7 +177,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot();
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.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 });
});
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.setContent(`
something above
@ -198,7 +228,9 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot();
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.setContent(`<div style="position:absolute;
top: 100px;
@ -211,31 +243,38 @@ module.exports.addTests = function({testRunner, expect, product}) {
const screenshot = await elementHandle.screenshot();
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>');
const elementHandle = await page.$('h1');
await page.evaluate(element => element.remove(), elementHandle);
const screenshotError = await elementHandle.screenshot().catch(error => error);
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>');
const div = await page.$('div');
const error = await div.screenshot().catch(e => e);
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>');
const elementHandle = await page.$('div');
const screenshot = await elementHandle.screenshot();
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>');
const elementHandle = await page.$('div');
const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-fractional-offset.png');
});
});
};
});

View File

@ -16,151 +16,176 @@
const utils = require('./utils');
const {waitEvent} = utils;
const expect = require('expect');
const {getTestState, setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, puppeteer}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Target', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
describe('Target', function() {
it('Browser.targets should return all of the targets', async({page, server, browser}) => {
// The pages will be the testing page and the original newtab page
const targets = browser.targets();
expect(targets.some(target => target.type() === 'page' &&
it('Browser.targets should return all of the targets', async() => {
const { browser } = getTestState();
// The pages will be the testing page and the original newtab page
const targets = browser.targets();
expect(targets.some(target => target.type() === 'page' &&
target.url() === 'about:blank')).toBeTruthy();
expect(targets.some(target => target.type() === 'browser')).toBeTruthy();
});
it('Browser.pages should return all of the pages', async({page, server, context}) => {
// The pages will be the testing page
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();
expect(targets.some(target => target.type() === 'browser')).toBeTruthy();
});
it('Browser.pages should return all of the pages', async() => {
const { page, context } = getTestState();
let allPages = await context.pages();
expect(allPages).toContain(page);
expect(allPages).toContain(otherPage);
// The pages will be the testing page
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() => {
const { browser } = getTestState();
const closePagePromise = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target.page())));
await otherPage.close();
expect(await closePagePromise).toBe(otherPage);
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() => {
const { page, browser } = getTestState();
allPages = await Promise.all(context.targets().map(target => target.page()));
expect(allPages).toContain(page);
expect(allPages).not.toContain(otherPage);
});
it_fails_ffox('should report when a service worker is created and destroyed', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
const createdTarget = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
// 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();
});
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');
expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js');
let allPages = await context.pages();
expect(allPages).toContain(page);
expect(allPages).toContain(otherPage);
const destroyedTarget = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target)));
await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister()));
expect(await destroyedTarget).toBe(await createdTarget);
});
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 closePagePromise = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target.page())));
await otherPage.close();
expect(await closePagePromise).toBe(otherPage);
const target = await context.waitForTarget(target => target.type() === 'service_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
});
it_fails_ffox('should create a worker from a shared worker', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
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 + '/');
allPages = await Promise.all(context.targets().map(target => target.page()));
expect(allPages).toContain(page);
expect(allPages).not.toContain(otherPage);
});
itFailsFirefox('should report when a service worker is created and destroyed', async() => {
const { page, server, context } = getTestState();
changedTarget = new Promise(fulfill => context.once('targetchanged', target => fulfill(target)));
await page.goto(server.EMPTY_PAGE);
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');
await page.goto(server.EMPTY_PAGE);
const createdTarget = new Promise(fulfill => context.once('targetcreated', target => fulfill(target)));
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);
});
it_fails_ffox('should not crash while redirecting if original request was missed', async({page, server, context}) => {
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();
});
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);
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
expect((await createdTarget).type()).toBe('service_worker');
expect((await createdTarget).url()).toBe(server.PREFIX + '/serviceworkers/empty/sw.js');
const destroyedTarget = new Promise(fulfill => context.once('targetdestroyed', target => fulfill(target)));
await page.evaluate(() => window.registrationPromise.then(registration => registration.unregister()));
expect(await destroyedTarget).toBe(await createdTarget);
});
itFailsFirefox('should create a worker from a service worker', async() => {
const { page, server, context } = getTestState();
await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html');
const target = await context.waitForTarget(target => target.type() === 'service_worker');
const worker = await target.worker();
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
});
itFailsFirefox('should create a worker from a shared worker', async() => {
const { page, server, context } = getTestState();
await page.goto(server.EMPTY_PAGE);
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]');
});
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', () => {
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;
const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE);
targetPromise.then(() => resolved = true);
@ -171,7 +196,9 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
expect(await target.page()).toBe(page);
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;
await browser.waitForTarget(target => target.url() === server.EMPTY_PAGE, {
timeout: 1
@ -179,4 +206,4 @@ module.exports.addTests = function({testRunner, expect, puppeteer}) {
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.
*/
module.exports.addTests = function({testRunner, expect, puppeteer}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const iPhone = puppeteer.devices['iPhone 6'];
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe_fails_ffox('Touchscreen', function() {
it('should tap the button', async({page, server}) => {
await page.emulate(iPhone);
await page.goto(server.PREFIX + '/input/button.html');
await page.tap('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
it('should report touches', async({page, server}) => {
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']);
});
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
describeFailsFirefox('Touchscreen', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('should tap the button', async() => {
const {puppeteer, page, server} = getTestState();
const iPhone = puppeteer.devices['iPhone 6'];
await page.emulate(iPhone);
await page.goto(server.PREFIX + '/input/button.html');
await page.tap('button');
expect(await page.evaluate(() => result)).toBe('Clicked');
});
};
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 path = require('path');
const expect = require('expect');
const {getTestState} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, puppeteer}) {
const {describe, xdescribe, fdescribe} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describeChromeOnly('Tracing', function() {
let outputFile;
let browser;
let page;
describe('Tracing', function() {
beforeEach(async function(state) {
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();
/* we manually manage the browser here as we want a new browser for each
* individual test, which isn't the default behaviour of getTestState()
*/
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({page, server, browser, outputFile}) => {
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({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');
});
beforeEach(async() => {
const {defaultBrowserOptions, puppeteer} = getTestState();
browser = await puppeteer.launch(defaultBrowserOptions);
page = await browser.newPage();
outputFile = path.join(__dirname, 'assets', 'trace.json');
});
};
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 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 = {
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) {
expect.extend({
toBeGolden: (testScreenshot, goldenFilePath) => {

View File

@ -15,14 +15,17 @@
*/
const utils = require('./utils');
const expect = require('expect');
const {getTestState,setupTestBrowserHooks,setupTestPageAndContextHooks} = require('./mocha-utils');
module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit, it_fails_ffox} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('waittask specs', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
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;
const waitFor = page.waitFor('div').then(() => found = true);
await page.goto(server.EMPTY_PAGE);
@ -31,7 +34,10 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitFor;
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;
const waitFor = page.waitFor('//div').then(() => found = true);
await page.goto(server.EMPTY_PAGE);
@ -40,47 +46,63 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitFor;
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>`);
let error = null;
await page.waitFor('/html/body/div').catch(e => error = e);
expect(error).toBeTruthy();
});
it('should timeout', async({page, server}) => {
it('should timeout', async() => {
const { page } = getTestState();
const startTime = Date.now();
const timeout = 42;
await page.waitFor(timeout);
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(`
(() => 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([
page.waitFor(() => window.innerWidth < 100),
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;
await page.waitFor({foo: 'bar'}).catch(e => error = e);
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);
});
});
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');
await page.evaluate(() => window.__FOO = 1);
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.waitForFunction(() => {
if (!window.__RELOADED)
@ -88,7 +110,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
return true;
});
});
it('should poll on interval', async({page, server}) => {
it('should poll on interval', async() => {
const { page } = getTestState();
let success = false;
const startTime = Date.now();
const polling = 100;
@ -100,7 +124,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await watchdog;
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;
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'mutation'})
.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 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'});
await page.evaluate(() => window.__FOO = 'hit');
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);
await page.goto(server.EMPTY_PAGE);
let error = null;
@ -124,7 +154,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
]);
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;
try {
await page.waitForFunction(() => !!document.body, {polling: 'unknown'});
@ -134,7 +166,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(error).toBeTruthy();
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;
try {
await page.waitForFunction(() => !!document.body, {polling: -10});
@ -144,13 +178,19 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(error).toBeTruthy();
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);
});
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();
});
it_fails_ffox('should accept ElementHandle arguments', async({page}) => {
itFailsFirefox('should accept ElementHandle arguments', async() => {
const { page } = getTestState();
await page.setContent('<div></div>');
const div = await page.$('div');
let resolved = false;
@ -159,21 +199,27 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await page.evaluate(element => element.remove(), div);
await waitForFunction;
});
it('should respect timeout', async({page}) => {
it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null;
await page.waitForFunction('false', {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('waiting for function failed: timeout');
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);
let error = null;
await page.waitForFunction('false').catch(e => error = e);
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
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(() => {
window.__counter = (window.__counter || 0) + 1;
return window.__injected;
@ -182,7 +228,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await page.evaluate(() => window.__injected = true);
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;
const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true);
await page.goto(server.EMPTY_PAGE);
@ -195,7 +243,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitForFunction;
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);
await page.goto(server.EMPTY_PAGE);
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));
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);
const frame = page.mainFrame();
await frame.waitForSelector('*');
@ -215,7 +267,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
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);
const [handle] = await Promise.all([
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');
});
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);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('div');
@ -235,7 +291,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
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);
const watchdog = page.waitForSelector('h3 div');
await page.evaluate(addElement, 'span');
@ -243,7 +301,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
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 utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const otherFrame = page.frames()[1];
@ -254,7 +314,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
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, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1];
@ -266,7 +328,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
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);
const frame = page.frames()[1];
let waitError = null;
@ -276,7 +340,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(waitError).toBeTruthy();
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;
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
await page.goto(server.EMPTY_PAGE);
@ -287,7 +353,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
await waitForSelector;
expect(boxFound).toBe(true);
});
it('should wait for visible', async({page, server}) => {
it('should wait for visible', async() => {
const { page } = getTestState();
let divFound = false;
const waitForSelector = page.waitForSelector('div', {visible: true}).then(() => divFound = true);
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(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;
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>`);
@ -309,7 +379,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(await waitForSelector).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;
await page.setContent(`<div style='display: block;'></div>`);
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(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;
await page.setContent(`<div style='display: block;'></div>`);
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(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>`);
let divRemoved = false;
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(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 });
expect(handle).toBe(null);
});
it('should respect timeout', async({page, server}) => {
it('should respect timeout', async() => {
const { page, puppeteer } = getTestState();
let error = null;
await page.waitForSelector('div', {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('waiting for selector "div" failed: timeout');
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>`);
let error = null;
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');
});
it('should respond to node attribute mutation', async({page, server}) => {
it('should respond to node attribute mutation', async() => {
const { page } = getTestState();
let divFound = false;
const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
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');
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');
await page.setContent(`<div class='zombo'>anything</div>`);
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;
await page.waitForSelector('.zombo', {timeout: 10}).catch(e => error = e);
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));
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>`);
const waitForXPath = page.waitForXPath('//p[normalize-space(.)="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;
await page.waitForXPath('//div', {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('waiting for XPath "//div" failed: timeout');
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, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1];
@ -404,7 +498,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
const eHandle = await waitForXPathPromise;
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);
const frame = page.frames()[1];
let waitError = null;
@ -414,7 +510,9 @@ module.exports.addTests = function({testRunner, expect, product, puppeteer}) {
expect(waitError).toBeTruthy();
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;
await page.setContent(`<div style='display: block;'></div>`);
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(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"]');
await page.setContent(`<div class='zombo'>anything</div>`);
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>`);
const text = await page.waitForXPath('//div/text()');
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>`);
const waitForXPath = page.waitForXPath('/html/body/div');
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 {waitEvent} = utils;
module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe, describe_fails_ffox} = testRunner;
const {it, fit, xit} = testRunner;
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describeFailsFirefox('Workers', function() {
setupTestBrowserHooks();
setupTestPageAndContextHooks();
it('Page.workers', async() => {
const { page, server } = getTestState();
describe_fails_ffox('Workers', function() {
it('Page.workers', async function({page, server}) {
await Promise.all([
new Promise(x => page.once('workercreated', x)),
page.goto(server.PREFIX + '/worker/worker.html')]);
const worker = page.workers()[0];
expect(worker.url()).toContain('worker.js');
await Promise.all([
new Promise(x => page.once('workercreated', x)),
page.goto(server.PREFIX + '/worker/worker.html')]);
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);
expect(page.workers().length).toBe(0);
});
it('should emit created and destroyed events', async function({page}) {
const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
const workerObj = await page.evaluateHandle(() => new Worker('data:text/javascript,1'));
const worker = await workerCreatedPromise;
const workerThisObj = await worker.evaluateHandle(() => this);
const workerDestroyedPromise = new Promise(x => page.once('workerdestroyed', x));
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
expect(await workerDestroyedPromise).toBe(worker);
const error = await workerThisObj.getProperty('self').catch(error => error);
expect(error.message).toContain('Most likely the worker has been closed.');
});
it('should report console logs', async function({page}) {
const [message] = await Promise.all([
waitEvent(page, 'console'),
page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`)),
]);
expect(message.text()).toBe('1');
expect(message.location()).toEqual({
url: 'data:text/javascript,console.log(1)',
lineNumber: 0,
columnNumber: 8,
});
});
it('should have JSHandles for console logs', async function({page}) {
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 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');
await page.goto(server.EMPTY_PAGE);
expect(page.workers().length).toBe(0);
});
it('should emit created and destroyed events', async() => {
const { page } = getTestState();
const workerCreatedPromise = new Promise(x => page.once('workercreated', x));
const workerObj = await page.evaluateHandle(() => new Worker('data:text/javascript,1'));
const worker = await workerCreatedPromise;
const workerThisObj = await worker.evaluateHandle(() => this);
const workerDestroyedPromise = new Promise(x => page.once('workerdestroyed', x));
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
expect(await workerDestroyedPromise).toBe(worker);
const error = await workerThisObj.getProperty('self').catch(error => error);
expect(error.message).toContain('Most likely the worker has been closed.');
});
it('should report console logs', async() => {
const { page } = getTestState();
const [message] = await Promise.all([
waitEvent(page, 'console'),
page.evaluate(() => new Worker(`data:text/javascript,console.log(1)`)),
]);
expect(message.text()).toBe('1');
expect(message.location()).toEqual({
url: 'data:text/javascript,console.log(1)',
lineNumber: 0,
columnNumber: 8,
});
});
};
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');
});
});