test: remove describes (#3274)
This commit is contained in:
parent
1148f0b906
commit
5c4f06703d
|
|
@ -3,7 +3,7 @@ module.exports = /** @type {import('@jest/types').Config.InitialOptions} */ ({
|
|||
maxWorkers: Math.ceil(require('os').cpus().length / 2),
|
||||
rootDir: './test',
|
||||
testEnvironment: './jest',
|
||||
testMatch: ['**/?(*.)jest.[jt]s'],
|
||||
testMatch: ['**/?(*.)(jest|spec).[jt]s'],
|
||||
testRunner: 'jest-circus/runner',
|
||||
testTimeout: 10000,
|
||||
globalSetup: './jest/setup.js',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CSSCoverage should work with complicated usecases 1`] = `
|
||||
exports[`should work with complicated usecases 1`] = `
|
||||
"[
|
||||
{
|
||||
\\"url\\": \\"http://localhost:<PORT>/csscoverage/involved.html\\",
|
||||
211
test/autowaiting-basic.spec.js
Normal file
211
test/autowaiting-basic.spec.js
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions;
|
||||
|
||||
it('should await navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await cross-process navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.CROSS_PROCESS_PREFIX + '/empty.html'}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await form-get on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?foo=bar', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="get">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await form-post on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="post">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should await navigation when assigning location', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.href = "${server.EMPTY_PAGE}"`).then(() => messages.push('evaluate')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|evaluate');
|
||||
});
|
||||
|
||||
it('should await navigation when assigning location twice', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?cancel', async (req, res) => { res.end('done'); });
|
||||
server.setRoute('/empty.html?override', async (req, res) => { messages.push('routeoverride'); res.end('done'); });
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}?cancel";
|
||||
window.location.href = "${server.EMPTY_PAGE}?override";
|
||||
`);
|
||||
messages.push('evaluate');
|
||||
expect(messages.join('|')).toBe('routeoverride|evaluate');
|
||||
});
|
||||
|
||||
it('should await navigation when evaluating reload', async({page, server}) => {
|
||||
const messages = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.reload()`).then(() => messages.push('evaluate')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|evaluate');
|
||||
});
|
||||
|
||||
it('should await navigating specified target', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<a href="${server.EMPTY_PAGE}" target=target>empty.html</a>
|
||||
<iframe name=target></iframe>
|
||||
`);
|
||||
const frame = page.frame({ name: 'target' });
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(frame.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
|
||||
it('should work with noWaitAfter: true', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async () => {});
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await page.click('a', { noWaitAfter: true });
|
||||
});
|
||||
|
||||
it('should work with dblclick noWaitAfter: true', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async () => {});
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await page.dblclick('a', { noWaitAfter: true });
|
||||
});
|
||||
|
||||
it('should work with waitForLoadState(load)', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.click('a').then(() => page.waitForLoadState('load')).then(() => messages.push('clickload')),
|
||||
page.waitForEvent('framenavigated').then(() => page.waitForLoadState('domcontentloaded')).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|clickload');
|
||||
});
|
||||
|
||||
it('should work with goto following click', async({page, server}) => {
|
||||
server.setRoute('/login.html', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`You are logged in`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.PREFIX}/login.html" method="get">
|
||||
<input type="text">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await page.fill('input[type=text]', 'admin');
|
||||
await page.click('input[type=submit]');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
it.skip(USES_HOOKS)('should report navigation in the log when clicking anchor', async({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX + '/frames/one-frame.html'}">click me</a>`);
|
||||
const __testHookAfterPointerAction = () => new Promise(f => setTimeout(f, 6000));
|
||||
const error = await page.click('a', { timeout: 5000, __testHookAfterPointerAction }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for scheduled navigations to finish');
|
||||
expect(error.message).toContain(`navigated to "${server.PREFIX + '/frames/one-frame.html'}"`);
|
||||
});
|
||||
67
test/autowaiting-no-hang.spec.js
Normal file
67
test/autowaiting-no-hang.spec.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions;
|
||||
|
||||
it('clicking on links which do not commit navigation', async({page, server, httpsServer}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href='${httpsServer.EMPTY_PAGE}'>foobar</a>`);
|
||||
await page.click('a');
|
||||
});
|
||||
|
||||
it('calling window.stop async', async({page, server, httpsServer}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
setTimeout(() => window.stop(), 100);
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
it('calling window.stop sync', async({page, server, httpsServer}) => {
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
window.stop();
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
|
||||
it('assigning location to about:blank', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(`window.location.href = "about:blank";`);
|
||||
});
|
||||
|
||||
it('assigning location to about:blank after non-about:blank', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}";
|
||||
window.location.href = "about:blank";`);
|
||||
});
|
||||
|
||||
it('calling window.open and window.close', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
const popup = window.open(window.location.href);
|
||||
popup.close();
|
||||
});
|
||||
});
|
||||
|
||||
it('opening a popup', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window._popup = window.open(window.location.href)),
|
||||
]);
|
||||
});
|
||||
|
|
@ -1,247 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Auto waiting', () => {
|
||||
it('should await navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
it('should await cross-process navigation when clicking anchor', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.CROSS_PROCESS_PREFIX + '/empty.html'}">empty.html</a>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
it('should await form-get on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?foo=bar', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="get">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
it('should await form-post on click', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.EMPTY_PAGE}" method="post">
|
||||
<input name="foo" value="bar">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await Promise.all([
|
||||
page.click('input[type=submit]').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
it('should await navigation when assigning location', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.href = "${server.EMPTY_PAGE}"`).then(() => messages.push('evaluate')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|evaluate');
|
||||
});
|
||||
it('should await navigation when assigning location twice', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html?cancel', async (req, res) => { res.end('done'); });
|
||||
server.setRoute('/empty.html?override', async (req, res) => { messages.push('routeoverride'); res.end('done'); });
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}?cancel";
|
||||
window.location.href = "${server.EMPTY_PAGE}?override";
|
||||
`);
|
||||
messages.push('evaluate');
|
||||
expect(messages.join('|')).toBe('routeoverride|evaluate');
|
||||
});
|
||||
it('should await navigation when evaluating reload', async({page, server}) => {
|
||||
const messages = [];
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
page.evaluate(`window.location.reload()`).then(() => messages.push('evaluate')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|navigated|evaluate');
|
||||
});
|
||||
it('should await navigating specified target', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<a href="${server.EMPTY_PAGE}" target=target>empty.html</a>
|
||||
<iframe name=target></iframe>
|
||||
`);
|
||||
const frame = page.frame({ name: 'target' });
|
||||
await Promise.all([
|
||||
page.click('a').then(() => messages.push('click')),
|
||||
page.waitForEvent('framenavigated').then(() => messages.push('navigated')),
|
||||
]);
|
||||
expect(frame.url()).toBe(server.EMPTY_PAGE);
|
||||
expect(messages.join('|')).toBe('route|navigated|click');
|
||||
});
|
||||
it('should work with noWaitAfter: true', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async () => {});
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await page.click('a', { noWaitAfter: true });
|
||||
});
|
||||
it('should work with dblclick noWaitAfter: true', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async () => {});
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await page.dblclick('a', { noWaitAfter: true });
|
||||
});
|
||||
it('should work with waitForLoadState(load)', async({page, server}) => {
|
||||
const messages = [];
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
messages.push('route');
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`<link rel='stylesheet' href='./one-style.css'>`);
|
||||
});
|
||||
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
await Promise.all([
|
||||
page.click('a').then(() => page.waitForLoadState('load')).then(() => messages.push('clickload')),
|
||||
page.waitForEvent('framenavigated').then(() => page.waitForLoadState('domcontentloaded')).then(() => messages.push('domcontentloaded')),
|
||||
]);
|
||||
expect(messages.join('|')).toBe('route|domcontentloaded|clickload');
|
||||
});
|
||||
it('should work with goto following click', async({page, server}) => {
|
||||
server.setRoute('/login.html', async (req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end(`You are logged in`);
|
||||
});
|
||||
|
||||
await page.setContent(`
|
||||
<form action="${server.PREFIX}/login.html" method="get">
|
||||
<input type="text">
|
||||
<input type="submit" value="Submit">
|
||||
</form>`);
|
||||
|
||||
await page.fill('input[type=text]', 'admin');
|
||||
await page.click('input[type=submit]');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
});
|
||||
it.skip(USES_HOOKS)('should report navigation in the log when clicking anchor', async({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX + '/frames/one-frame.html'}">click me</a>`);
|
||||
const __testHookAfterPointerAction = () => new Promise(f => setTimeout(f, 6000));
|
||||
const error = await page.click('a', { timeout: 5000, __testHookAfterPointerAction }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for scheduled navigations to finish');
|
||||
expect(error.message).toContain(`navigated to "${server.PREFIX + '/frames/one-frame.html'}"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Auto waiting should not hang when', () => {
|
||||
it('clicking on links which do not commit navigation', async({page, server, httpsServer}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href='${httpsServer.EMPTY_PAGE}'>foobar</a>`);
|
||||
await page.click('a');
|
||||
});
|
||||
it('calling window.stop async', async({page, server, httpsServer}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
setTimeout(() => window.stop(), 100);
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
it('calling window.stop sync', async({page, server, httpsServer}) => {
|
||||
await page.evaluate((url) => {
|
||||
window.location.href = url;
|
||||
window.stop();
|
||||
}, server.EMPTY_PAGE);
|
||||
});
|
||||
it('assigning location to about:blank', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(`window.location.href = "about:blank";`);
|
||||
});
|
||||
it('assigning location to about:blank after non-about:blank', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {});
|
||||
await page.evaluate(`
|
||||
window.location.href = "${server.EMPTY_PAGE}";
|
||||
window.location.href = "about:blank";`);
|
||||
});
|
||||
it('calling window.open and window.close', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
const popup = window.open(window.location.href);
|
||||
popup.close();
|
||||
});
|
||||
});
|
||||
it('opening a popup', async function({page, server}) {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(() => window._popup = window.open(window.location.href)),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
/**
|
||||
* Copyright 2020 Microsoft Corporation. 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 {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
describe('Browser.newPage', function() {
|
||||
it('should create new page', async function({browser}) {
|
||||
const page1 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
|
||||
const page2 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(2);
|
||||
|
||||
await page1.close();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
|
||||
await page2.close();
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
});
|
||||
it('should throw upon second create new page', async function({browser}) {
|
||||
const page = await browser.newPage();
|
||||
let error;
|
||||
await page.context().newPage().catch(e => error = e);
|
||||
await page.close();
|
||||
expect(error.message).toContain('Please use browser.newContext()');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Browser.version', function() {
|
||||
it('should work', async function({browser}) {
|
||||
const version = browser.version();
|
||||
if (CHROMIUM)
|
||||
expect(version.match(/^\d+\.\d+\.\d+\.\d+$/)).toBeTruthy();
|
||||
else
|
||||
expect(version.match(/^\d+\.\d+$/)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
47
test/browser.spec.js
Normal file
47
test/browser.spec.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Copyright 2020 Microsoft Corporation. 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 {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
it('should create new page', async function({browser}) {
|
||||
const page1 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
|
||||
const page2 = await browser.newPage();
|
||||
expect(browser.contexts().length).toBe(2);
|
||||
|
||||
await page1.close();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
|
||||
await page2.close();
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
});
|
||||
|
||||
it('should throw upon second create new page', async function({browser}) {
|
||||
const page = await browser.newPage();
|
||||
let error;
|
||||
await page.context().newPage().catch(e => error = e);
|
||||
await page.close();
|
||||
expect(error.message).toContain('Please use browser.newContext()');
|
||||
});
|
||||
|
||||
it('version should work', async function({browser}) {
|
||||
const version = browser.version();
|
||||
if (CHROMIUM)
|
||||
expect(version.match(/^\d+\.\d+\.\d+\.\d+$/)).toBeTruthy();
|
||||
else
|
||||
expect(version.match(/^\d+\.\d+$/)).toBeTruthy();
|
||||
});
|
||||
329
test/browsercontext-add-cookies.spec.js
Normal file
329
test/browsercontext-add-cookies.spec.js
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, WIN} = testOptions;
|
||||
|
||||
it('should work', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'password',
|
||||
value: '123456'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toEqual('password=123456');
|
||||
});
|
||||
it('should roundtrip cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
const documentCookie = await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
return document.cookie;
|
||||
}, date);
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
const cookies = await context.cookies();
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await context.addCookies(cookies);
|
||||
expect(await context.cookies()).toEqual(cookies);
|
||||
});
|
||||
it('should send cookie header', async({server, context}) => {
|
||||
let cookie = '';
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie;
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'cookie', value: 'value'}]);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('cookie=value');
|
||||
});
|
||||
it('should isolate cookies in browser contexts', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page2value'}]);
|
||||
|
||||
const cookies1 = await context.cookies();
|
||||
const cookies2 = await anotherContext.cookies();
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies2.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('isolatecookie');
|
||||
expect(cookies1[0].value).toBe('page1value');
|
||||
expect(cookies2[0].name).toBe('isolatecookie');
|
||||
expect(cookies2[0].value).toBe('page2value');
|
||||
await anotherContext.close();
|
||||
});
|
||||
it('should isolate session cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'session=value');
|
||||
res.end();
|
||||
});
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
}
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies.map(c => c.value).join(',')).toBe('value');
|
||||
}
|
||||
{
|
||||
const context2 = await browser.newContext();
|
||||
const page = await context2.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies[0] && cookies[0].name).toBe(undefined);
|
||||
await context2.close();
|
||||
}
|
||||
});
|
||||
it('should isolate persistent cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'persistent=persistent-value; max-age=3600');
|
||||
res.end();
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
|
||||
const context1 = context;
|
||||
const context2 = await browser.newContext();
|
||||
const [page1, page2] = await Promise.all([context1.newPage(), context2.newPage()]);
|
||||
await Promise.all([page1.goto(server.EMPTY_PAGE), page2.goto(server.EMPTY_PAGE)]);
|
||||
const [cookies1, cookies2] = await Promise.all([context1.cookies(), context2.cookies()]);
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('persistent');
|
||||
expect(cookies1[0].value).toBe('persistent-value');
|
||||
expect(cookies2.length).toBe(0);
|
||||
await context2.close();
|
||||
});
|
||||
it('should isolate send cookie header', async({server, context, browser}) => {
|
||||
let cookie = [];
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie || '';
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'sendcookie', value: 'value'}]);
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('sendcookie=value');
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it.slow()('should isolate cookies between launches', async({browserType, server, defaultBrowserOptions}) => {
|
||||
const browser1 = await browserType.launch(defaultBrowserOptions);
|
||||
const context1 = await browser1.newContext();
|
||||
await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]);
|
||||
await browser1.close();
|
||||
|
||||
const browser2 = await browserType.launch(defaultBrowserOptions);
|
||||
const context2 = await browser2.newContext();
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies.length).toBe(0);
|
||||
await browser2.close();
|
||||
});
|
||||
it('should set multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-1',
|
||||
value: '123456'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-2',
|
||||
value: 'bar'
|
||||
}]);
|
||||
expect(await page.evaluate(() => {
|
||||
const cookies = document.cookie.split(';');
|
||||
return cookies.map(cookie => cookie.trim()).sort();
|
||||
})).toEqual([
|
||||
'multiple-1=123456',
|
||||
'multiple-2=bar',
|
||||
]);
|
||||
});
|
||||
it('should have |expires| set to |-1| for session cookies', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'expires',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies[0].expires).toBe(-1);
|
||||
});
|
||||
it('should set cookie with reasonable defaults', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'defaults',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.sort((a, b) => a.name.localeCompare(b.name))).toEqual([{
|
||||
name: 'defaults',
|
||||
value: '123456',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set a cookie with a path', async({context, page, server}) => {
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await context.addCookies([{
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
}]);
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
});
|
||||
it('should not set a cookie with blank page URL', async function({context, server}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([
|
||||
{url: server.EMPTY_PAGE, name: 'example-cookie', value: 'best'},
|
||||
{url: 'about:blank', name: 'example-cookie-blank', value: 'best'}
|
||||
]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain(
|
||||
`Blank page can not have cookie "example-cookie-blank"`
|
||||
);
|
||||
});
|
||||
it('should not set a cookie on a data URL page', async function({context}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([{url: 'data:,Hello%2C%20World!', name: 'example-cookie', value: 'best'}]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Data URL page can not have cookie "example-cookie"');
|
||||
});
|
||||
it('should default to setting secure cookie for HTTPS websites', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const SECURE_URL = 'https://example.com';
|
||||
await context.addCookies([{
|
||||
url: SECURE_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(SECURE_URL);
|
||||
expect(cookie.secure).toBe(true);
|
||||
});
|
||||
it('should be able to set unsecure cookie for HTTP website', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const HTTP_URL = 'http://example.com';
|
||||
await context.addCookies([{
|
||||
url: HTTP_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(HTTP_URL);
|
||||
expect(cookie.secure).toBe(false);
|
||||
});
|
||||
it('should set a cookie on a different domain', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: 'https://www.example.com',
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
expect(await context.cookies('https://www.example.com')).toEqual([{
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
domain: 'www.example.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set cookies for a frame', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([
|
||||
{url: server.PREFIX, name: 'frame-cookie', value: 'value'}
|
||||
]);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.PREFIX + '/grid.html');
|
||||
|
||||
expect(await page.frames()[1].evaluate('document.cookie')).toBe('frame-cookie=value');
|
||||
});
|
||||
it('should(not) block third party cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await page.frames()[1].evaluate(`document.cookie = 'username=John Doe'`);
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = CHROMIUM || FFOX;
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
"domain": "127.0.0.1",
|
||||
"expires": -1,
|
||||
"httpOnly": false,
|
||||
"name": "username",
|
||||
"path": "/",
|
||||
"sameSite": "None",
|
||||
"secure": false,
|
||||
"value": "John Doe"
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
expect(cookies).toEqual([]);
|
||||
}
|
||||
});
|
||||
232
test/browsercontext-basic.spec.js
Normal file
232
test/browsercontext-basic.spec.js
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it('should create new context', async function({browser}) {
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
const context = await browser.newContext();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
expect(browser.contexts().indexOf(context) !== -1).toBe(true);
|
||||
await context.close();
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
});
|
||||
|
||||
it('window.open should use parent tab context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(popup.context()).toBe(context);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should isolate localStorage and cookies', async function({browser, server}) {
|
||||
// Create two incognito contexts.
|
||||
const context1 = await browser.newContext();
|
||||
const context2 = await browser.newContext();
|
||||
expect(context1.pages().length).toBe(0);
|
||||
expect(context2.pages().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.pages().length).toBe(1);
|
||||
expect(context2.pages().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.pages().length).toBe(1);
|
||||
expect(context2.pages().length).toBe(1);
|
||||
expect(context1.pages()[0]).toBe(page1);
|
||||
expect(context2.pages()[0]).toBe(page2);
|
||||
|
||||
// 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.contexts().length).toBe(0);
|
||||
});
|
||||
|
||||
it('should propagate default viewport to the page', async({ browser }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should make a copy of default viewport', async({ browser }) => {
|
||||
const viewport = { width: 456, height: 789 };
|
||||
const context = await browser.newContext({ viewport });
|
||||
viewport.width = 567;
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should respect deviceScaleFactor', async({ browser }) => {
|
||||
const context = await browser.newContext({ deviceScaleFactor: 3 });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate('window.devicePixelRatio')).toBe(3);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should not allow deviceScaleFactor with null viewport', async({ browser }) => {
|
||||
const error = await browser.newContext({ viewport: null, deviceScaleFactor: 1 }).catch(e => e);
|
||||
expect(error.message).toContain('"deviceScaleFactor" option is not supported with null "viewport"');
|
||||
});
|
||||
|
||||
it('should not allow isMobile with null viewport', async({ browser }) => {
|
||||
const error = await browser.newContext({ viewport: null, isMobile: true }).catch(e => e);
|
||||
expect(error.message).toContain('"isMobile" option is not supported with null "viewport"');
|
||||
});
|
||||
|
||||
it('close() should work for empty context', async({ browser }) => {
|
||||
const context = await browser.newContext();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('close() should abort waitForEvent', async({ browser }) => {
|
||||
const context = await browser.newContext();
|
||||
const promise = context.waitForEvent('page').catch(e => e);
|
||||
await context.close();
|
||||
let error = await promise;
|
||||
expect(error.message).toContain('Context closed');
|
||||
});
|
||||
|
||||
it('close() should be callable twice', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
await Promise.all([
|
||||
context.close(),
|
||||
context.close(),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should not report frameless pages on error', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.end(`<a href="${server.EMPTY_PAGE}" target="_blank">Click me</a>`);
|
||||
});
|
||||
let popup;
|
||||
context.on('page', p => popup = p);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.click('"Click me"');
|
||||
await context.close();
|
||||
if (popup) {
|
||||
// This races on Firefox :/
|
||||
expect(popup.isClosed()).toBeTruthy();
|
||||
expect(popup.mainFrame()).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should return all of the pages', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const second = await context.newPage();
|
||||
const allPages = context.pages();
|
||||
expect(allPages.length).toBe(2);
|
||||
expect(allPages).toContain(page);
|
||||
expect(allPages).toContain(second);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should close all belonging pages once closing context', async function({browser}) {
|
||||
const context = await browser.newContext();
|
||||
await context.newPage();
|
||||
expect(context.pages().length).toBe(1);
|
||||
|
||||
await context.close();
|
||||
expect(context.pages().length).toBe(0);
|
||||
});
|
||||
|
||||
it('should disable javascript', async({browser}) => {
|
||||
{
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
await context.close();
|
||||
}
|
||||
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
expect(await page.evaluate('something')).toBe('forbidden');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should be able to navigate after disabling javascript', async({browser, server}) => {
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with offline option', async({browser, server}) => {
|
||||
const context = await browser.newContext({offline: true});
|
||||
const page = await context.newPage();
|
||||
let error = null;
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
await context.setOffline(false);
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should emulate navigator.onLine', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
|
||||
await context.setOffline(true);
|
||||
expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
|
||||
await context.setOffline(false);
|
||||
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
50
test/browsercontext-clearcookies.spec.js
Normal file
50
test/browsercontext-clearcookies.spec.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, WIN} = testOptions;
|
||||
|
||||
it('should clear cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await page.reload();
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
|
||||
it('should isolate cookies when clearing', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'page1cookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'page2cookie', value: 'page2value'}]);
|
||||
|
||||
expect((await context.cookies()).length).toBe(1);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await context.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await anotherContext.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(0);
|
||||
await anotherContext.close();
|
||||
});
|
||||
167
test/browsercontext-cookies.spec.js
Normal file
167
test/browsercontext-cookies.spec.js
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, WIN} = testOptions;
|
||||
|
||||
it('should return no cookies in pristine browser context', async({context, page, server}) => {
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
});
|
||||
|
||||
it('should get a cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
return document.cookie;
|
||||
});
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should get a non-session cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
const documentCookie = await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
return document.cookie;
|
||||
}, date);
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: date / 1000,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should properly report httpOnly cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;HttpOnly; Path=/');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].httpOnly).toBe(true);
|
||||
});
|
||||
|
||||
it.fail(WEBKIT && WIN)('should properly report "Strict" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Strict');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Strict');
|
||||
});
|
||||
|
||||
it.fail(WEBKIT && WIN)('should properly report "Lax" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Lax');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Lax');
|
||||
});
|
||||
|
||||
it('should get multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
document.cookie = 'password=1234';
|
||||
return document.cookie.split('; ').sort().join('; ');
|
||||
});
|
||||
const cookies = await context.cookies();
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(documentCookie).toBe('password=1234; username=John Doe');
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
name: 'password',
|
||||
value: '1234',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should get cookies from multiple urls', async({context}) => {
|
||||
await context.addCookies([{
|
||||
url: 'https://foo.com',
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
}, {
|
||||
url: 'https://bar.com',
|
||||
name: 'catto',
|
||||
value: 'purrs',
|
||||
}, {
|
||||
url: 'https://baz.com',
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
}]);
|
||||
const cookies = await context.cookies(['https://foo.com', 'https://baz.com']);
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(cookies).toEqual([{
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
domain: 'baz.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}, {
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
domain: 'foo.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
76
test/browsercontext-credentials.spec.js
Normal file
76
test/browsercontext-credentials.spec.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it.fail(CHROMIUM && !HEADLESS)('should fail without credentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.fail(CHROMIUM && !HEADLESS)('should work with setHTTPCredentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
let response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.setHTTPCredentials({ username: 'user', password: 'pass' });
|
||||
response = await page.reload();
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with correct credentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.fail(CHROMIUM && !HEADLESS)('should fail with wrong credentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'foo', password: 'bar' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should return resource body', async({browser, server}) => {
|
||||
server.setAuth('/playground.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.PREFIX + '/playground.html');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(await page.title()).toBe("Playground");
|
||||
expect((await response.body()).toString()).toContain("Playground");
|
||||
await context.close();
|
||||
});
|
||||
103
test/browsercontext-csp.spec.js
Normal file
103
test/browsercontext-csp.spec.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it('should bypass CSP meta tag', async({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag.
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
// By-pass CSP and try one more time.
|
||||
{
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass CSP header', async({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag.
|
||||
server.setCSP('/empty.html', 'default-src "self"');
|
||||
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
// By-pass CSP and try one more time.
|
||||
{
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass after cross-process navigation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should bypass CSP in iframes as well', async({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag in an iframe.
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await frame.evaluate(() => window.__injected)).toBe(undefined);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
// By-pass CSP and try one more time.
|
||||
{
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await frame.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
58
test/browsercontext-device.spec.js
Normal file
58
test/browsercontext-device.spec.js
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it.skip(FFOX)('should work', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should support clicking', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.style.marginTop = '200px', button);
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should scroll to click', async({browser, server}) => {
|
||||
const context = await browser.newContext({
|
||||
viewport: {
|
||||
width: 400,
|
||||
height: 400,
|
||||
},
|
||||
deviceScaleFactor: 1,
|
||||
isMobile: true
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
const element = await page.$('#button-91');
|
||||
await element.click();
|
||||
expect(await element.textContent()).toBe('clicked');
|
||||
await context.close();
|
||||
});
|
||||
85
test/browsercontext-expose-function.spec.js
Normal file
85
test/browsercontext-expose-function.spec.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it('expose binding should work', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
let bindingSource;
|
||||
await context.exposeBinding('add', (source, a, b) => {
|
||||
bindingSource = source;
|
||||
return a + b;
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const result = await page.evaluate(async function() {
|
||||
return add(5, 6);
|
||||
});
|
||||
expect(bindingSource.context).toBe(context);
|
||||
expect(bindingSource.page).toBe(page);
|
||||
expect(bindingSource.frame).toBe(page.mainFrame());
|
||||
expect(result).toEqual(11);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.exposeFunction('add', (a, b) => a + b);
|
||||
const page = await context.newPage();
|
||||
await page.exposeFunction('mul', (a, b) => a * b);
|
||||
await context.exposeFunction('sub', (a, b) => a - b);
|
||||
await context.exposeBinding('addHandle', async ({ frame }, a, b) => {
|
||||
const handle = await frame.evaluateHandle(([a, b]) => a + b, [a, b]);
|
||||
return handle;
|
||||
});
|
||||
const result = await page.evaluate(async function() {
|
||||
return { mul: await mul(9, 4), add: await add(9, 4), sub: await sub(9, 4), addHandle: await addHandle(5, 6) };
|
||||
});
|
||||
expect(result).toEqual({ mul: 36, add: 13, sub: 5, addHandle: 11 });
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should throw for duplicate registrations', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.exposeFunction('foo', () => {});
|
||||
await context.exposeFunction('bar', () => {});
|
||||
let error = await context.exposeFunction('foo', () => {}).catch(e => e);
|
||||
expect(error.message).toContain('Function "foo" has been already registered');
|
||||
const page = await context.newPage();
|
||||
error = await page.exposeFunction('foo', () => {}).catch(e => e);
|
||||
expect(error.message).toContain('Function "foo" has been already registered in the browser context');
|
||||
await page.exposeFunction('baz', () => {});
|
||||
error = await context.exposeFunction('baz', () => {}).catch(e => e);
|
||||
expect(error.message).toContain('Function "baz" has been already registered in one of the pages');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should be callable from-inside addInitScript', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
let args = [];
|
||||
await context.exposeFunction('woof', function(arg) {
|
||||
args.push(arg);
|
||||
});
|
||||
await context.addInitScript(() => woof('context'));
|
||||
const page = await context.newPage();
|
||||
await page.addInitScript(() => woof('page'));
|
||||
args = [];
|
||||
await page.reload();
|
||||
expect(args).toEqual(['context', 'page']);
|
||||
await context.close();
|
||||
});
|
||||
140
test/browsercontext-locale.spec.js
Normal file
140
test/browsercontext-locale.spec.js
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it('should affect accept-language header', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['accept-language'].substr(0, 5)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should affect navigator.language', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should format number', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString())).toBe('1,000,000.5');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString().replace(/\s/g, ' '))).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should format date', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US', timezoneId: 'America/Los_Angeles' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const formatted = 'Sat Nov 19 2016 10:12:34 GMT-0800 (Pacific Standard Time)';
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(formatted);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'de-DE', timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(
|
||||
'Sat Nov 19 2016 19:12:34 GMT+0100 (Mitteleuropäische Normalzeit)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should format number in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.result);
|
||||
expect(result).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should affect navigator.language in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.initialNavigatorLanguage);
|
||||
expect(result).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should be isolated between contexts', async({browser, server}) => {
|
||||
const context1 = await browser.newContext({ locale: 'en-US' });
|
||||
const promises = [];
|
||||
// By default firefox limits number of child web processes to 8.
|
||||
for (let i = 0; i< 8; i++)
|
||||
promises.push(context1.newPage());
|
||||
await Promise.all(promises);
|
||||
|
||||
const context2 = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page2 = await context2.newPage();
|
||||
|
||||
const localeNumber = () => (1000000.50).toLocaleString();
|
||||
const numbers = await Promise.all(context1.pages().map(page => page.evaluate(localeNumber)));
|
||||
|
||||
numbers.forEach(value => expect(value).toBe('1,000,000.5'));
|
||||
expect(await page2.evaluate(localeNumber)).toBe('1 000 000,5');
|
||||
|
||||
await Promise.all([
|
||||
context1.close(),
|
||||
context2.close()
|
||||
]);
|
||||
});
|
||||
187
test/browsercontext-page-event.spec.js
Normal file
187
test/browsercontext-page-event.spec.js
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it('should have url', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(otherPage.url()).toBe(server.EMPTY_PAGE);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should have url after domcontentloaded', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
|
||||
]);
|
||||
await otherPage.waitForLoadState('domcontentloaded');
|
||||
expect(otherPage.url()).toBe(server.EMPTY_PAGE);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should have about:blank url with domcontentloaded', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), 'about:blank')
|
||||
]);
|
||||
await otherPage.waitForLoadState('domcontentloaded');
|
||||
expect(otherPage.url()).toBe('about:blank');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should have about:blank for empty url with domcontentloaded', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(() => window.open())
|
||||
]);
|
||||
await otherPage.waitForLoadState('domcontentloaded');
|
||||
expect(otherPage.url()).toBe('about:blank');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should report when a new page is created and closed', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html'),
|
||||
]);
|
||||
// The url is about:blank in FF when 'page' event is fired.
|
||||
expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX);
|
||||
expect(await otherPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
|
||||
expect(await otherPage.$('body')).toBeTruthy();
|
||||
|
||||
let allPages = context.pages();
|
||||
expect(allPages).toContain(page);
|
||||
expect(allPages).toContain(otherPage);
|
||||
|
||||
let closeEventReceived;
|
||||
otherPage.once('close', () => closeEventReceived = true);
|
||||
await otherPage.close();
|
||||
expect(closeEventReceived).toBeTruthy();
|
||||
|
||||
allPages = context.pages();
|
||||
expect(allPages).toContain(page);
|
||||
expect(allPages).not.toContain(otherPage);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should report initialized pages', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const pagePromise = context.waitForEvent('page');
|
||||
context.newPage();
|
||||
const newPage = await pagePromise;
|
||||
expect(newPage.url()).toBe('about:blank');
|
||||
|
||||
const popupPromise = context.waitForEvent('page');
|
||||
const evaluatePromise = newPage.evaluate(() => window.open('about:blank'));
|
||||
const popup = await popupPromise;
|
||||
expect(popup.url()).toBe('about:blank');
|
||||
await evaluatePromise;
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should not crash while redirecting of original request was missed', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
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.
|
||||
const [newPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.PREFIX + '/one-style.html'),
|
||||
server.waitForRequest('/one-style.css')
|
||||
]);
|
||||
// Issue a redirect.
|
||||
serverResponse.writeHead(302, { location: '/injectedstyle.css' });
|
||||
serverResponse.end();
|
||||
await newPage.waitForLoadState('domcontentloaded');
|
||||
expect(newPage.url()).toBe(server.PREFIX + '/one-style.html');
|
||||
// Cleanup.
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should have an opener', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.goto(server.PREFIX + '/popup/window-open.html')
|
||||
]);
|
||||
expect(popup.url()).toBe(server.PREFIX + '/popup/popup.html');
|
||||
expect(await popup.opener()).toBe(page);
|
||||
expect(await page.opener()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should fire page lifecycle events', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const events = [];
|
||||
context.on('page', async page => {
|
||||
events.push('CREATED: ' + page.url());
|
||||
page.on('close', () => events.push('DESTROYED: ' + page.url()));
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.close();
|
||||
expect(events).toEqual([
|
||||
'CREATED: about:blank',
|
||||
`DESTROYED: ${server.EMPTY_PAGE}`
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
it.fail(WEBKIT)('should work with Shift-clicking', async({browser, server}) => {
|
||||
// WebKit: Shift+Click does not open a new window.
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a', { modifiers: ['Shift'] }),
|
||||
]);
|
||||
expect(await popup.opener()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
it.fail(WEBKIT || FFOX)('should work with Ctrl-clicking', async({browser, server}) => {
|
||||
// Firefox: reports an opener in this case.
|
||||
// WebKit: Ctrl+Click does not open a new tab.
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a', { modifiers: [ MAC ? 'Meta' : 'Control'] }),
|
||||
]);
|
||||
expect(await popup.opener()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
111
test/browsercontext-route.spec.js
Normal file
111
test/browsercontext-route.spec.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it('should intercept', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted = true;
|
||||
const request = route.request();
|
||||
expect(request.url()).toContain('empty.html');
|
||||
expect(request.headers()['user-agent']).toBeTruthy();
|
||||
expect(request.method()).toBe('GET');
|
||||
expect(request.postData()).toBe(null);
|
||||
expect(request.isNavigationRequest()).toBe(true);
|
||||
expect(request.resourceType()).toBe('document');
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe('about:blank');
|
||||
route.continue();
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(intercepted).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should unroute', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
||||
let intercepted = [];
|
||||
const handler1 = route => {
|
||||
intercepted.push(1);
|
||||
route.continue();
|
||||
};
|
||||
await context.route('**/empty.html', handler1);
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted.push(2);
|
||||
route.continue();
|
||||
});
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted.push(3);
|
||||
route.continue();
|
||||
});
|
||||
await context.route('**/*', route => {
|
||||
intercepted.push(4);
|
||||
route.continue();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([1]);
|
||||
|
||||
intercepted = [];
|
||||
await context.unroute('**/empty.html', handler1);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([2]);
|
||||
|
||||
intercepted = [];
|
||||
await context.unroute('**/empty.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([4]);
|
||||
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should yield to page.route', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'context' });
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'page' });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(await response.text()).toBe('page');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should fall back to context.route', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'context' });
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.route('**/non-empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'page' });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(await response.text()).toBe('context');
|
||||
await context.close();
|
||||
});
|
||||
72
test/browsercontext-timezone-id.spec.js
Normal file
72
test/browsercontext-timezone-id.spec.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it('should work', async ({ browser }) => {
|
||||
const func = () => new Date(1479579154987).toString();
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Jamaica' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Pacific/Honolulu' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Buenos_Aires' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw for invalid timezone IDs when creating pages', async({browser}) => {
|
||||
for (const timezoneId of ['Foo/Bar', 'Baz/Qux']) {
|
||||
let error = null;
|
||||
const context = await browser.newContext({ timezoneId });
|
||||
const page = await context.newPage().catch(e => error = e);
|
||||
expect(error.message).toContain(`Invalid timezone ID: ${timezoneId}`);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Moscow' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
88
test/browsercontext-user-agent.spec.js
Normal file
88
test/browsercontext-user-agent.spec.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
it('should work', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ userAgent: 'foobar' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should work for subframes', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ userAgent: 'foobar' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
utils.attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should emulate device user-agent', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => navigator.userAgent)).not.toContain('iPhone');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ userAgent: devices['iPhone 6'].userAgent });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should make a copy of default options', async({browser, server}) => {
|
||||
const options = { userAgent: 'foobar' };
|
||||
const context = await browser.newContext(options);
|
||||
options.userAgent = 'wrong';
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
});
|
||||
130
test/browsercontext-viewport-mobile.spec.js
Normal file
130
test/browsercontext-viewport-mobile.spec.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it.skip(FFOX)('should support mobile emulation', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(400);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should support touch emulation', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
|
||||
await context.close();
|
||||
|
||||
function dispatchTouch() {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
window.ontouchstart = function(e) {
|
||||
fulfill('Received touch');
|
||||
};
|
||||
window.dispatchEvent(new Event('touchstart'));
|
||||
|
||||
fulfill('Did not receive touch');
|
||||
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should be detectable by Modernizr', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should detect touch when applying viewport with touches', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
|
||||
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should support landscape emulation', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const iPhoneLandscape = playwright.devices['iPhone 6 landscape'];
|
||||
const context1 = await browser.newContext({ ...iPhone });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
|
||||
const context2 = await browser.newContext({ ...iPhoneLandscape });
|
||||
const page2 = await context2.newPage();
|
||||
expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true);
|
||||
await context1.close();
|
||||
await context2.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should support window.orientation emulation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.orientation)).toBe(0);
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect(await page.evaluate(() => window.orientation)).toBe(90);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should fire orientationchange event', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
await page.evaluate(() => {
|
||||
window.counter = 0;
|
||||
window.addEventListener('orientationchange', () => console.log(++window.counter));
|
||||
});
|
||||
|
||||
const event1 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect((await event1).text()).toBe('1');
|
||||
|
||||
const event2 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 300, height: 400});
|
||||
expect((await event2).text()).toBe('2');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('default mobile viewports to 980 width', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.skip(FFOX)('respect meta viewport tag', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(320);
|
||||
await context.close();
|
||||
});
|
||||
106
test/browsercontext-viewport.spec.js
Normal file
106
test/browsercontext-viewport.spec.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it('should get the proper default viewport size', async({page, server}) => {
|
||||
await utils.verifyViewport(page, 1280, 720);
|
||||
});
|
||||
|
||||
it('should set the proper viewport size', async({page, server}) => {
|
||||
await utils.verifyViewport(page, 1280, 720);
|
||||
await page.setViewportSize({width: 123, height: 456});
|
||||
await utils.verifyViewport(page, 123, 456);
|
||||
});
|
||||
|
||||
it('should return correct outerWidth and outerHeight', async({page}) => {
|
||||
const size = await page.evaluate(() => {
|
||||
return {
|
||||
innerWidth: window.innerWidth,
|
||||
innerHeight: window.innerHeight,
|
||||
outerWidth: window.outerWidth,
|
||||
outerHeight: window.outerHeight,
|
||||
};
|
||||
});
|
||||
expect(size.innerWidth).toBe(1280);
|
||||
expect(size.innerHeight).toBe(720);
|
||||
expect(size.outerWidth >= size.innerWidth).toBeTruthy();
|
||||
expect(size.outerHeight >= size.innerHeight).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should emulate device width', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(true);
|
||||
});
|
||||
|
||||
it('should emulate device height', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not have touch by default', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
|
||||
});
|
||||
|
||||
it('should support touch with null viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: null, hasTouch: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should report null viewportSize when given null viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: null });
|
||||
const page = await context.newPage();
|
||||
expect(page.viewportSize()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
|
|
@ -1,749 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, MAC, CHANNEL, HEADLESS} = testOptions;
|
||||
const {devices} = require('..');
|
||||
|
||||
describe('BrowserContext', function() {
|
||||
it('should create new context', async function({browser}) {
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
const context = await browser.newContext();
|
||||
expect(browser.contexts().length).toBe(1);
|
||||
expect(browser.contexts().indexOf(context) !== -1).toBe(true);
|
||||
await context.close();
|
||||
expect(browser.contexts().length).toBe(0);
|
||||
});
|
||||
it('window.open should use parent tab context', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(popup.context()).toBe(context);
|
||||
await context.close();
|
||||
});
|
||||
it('should isolate localStorage and cookies', async function({browser, server}) {
|
||||
// Create two incognito contexts.
|
||||
const context1 = await browser.newContext();
|
||||
const context2 = await browser.newContext();
|
||||
expect(context1.pages().length).toBe(0);
|
||||
expect(context2.pages().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.pages().length).toBe(1);
|
||||
expect(context2.pages().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.pages().length).toBe(1);
|
||||
expect(context2.pages().length).toBe(1);
|
||||
expect(context1.pages()[0]).toBe(page1);
|
||||
expect(context2.pages()[0]).toBe(page2);
|
||||
|
||||
// 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.contexts().length).toBe(0);
|
||||
});
|
||||
it('should propagate default viewport to the page', async({ browser }) => {
|
||||
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
it('should make a copy of default viewport', async({ browser }) => {
|
||||
const viewport = { width: 456, height: 789 };
|
||||
const context = await browser.newContext({ viewport });
|
||||
viewport.width = 567;
|
||||
const page = await context.newPage();
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
await context.close();
|
||||
});
|
||||
it('should respect deviceScaleFactor', async({ browser }) => {
|
||||
const context = await browser.newContext({ deviceScaleFactor: 3 });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate('window.devicePixelRatio')).toBe(3);
|
||||
await context.close();
|
||||
});
|
||||
it('should not allow deviceScaleFactor with null viewport', async({ browser }) => {
|
||||
const error = await browser.newContext({ viewport: null, deviceScaleFactor: 1 }).catch(e => e);
|
||||
expect(error.message).toContain('"deviceScaleFactor" option is not supported with null "viewport"');
|
||||
});
|
||||
it('should not allow isMobile with null viewport', async({ browser }) => {
|
||||
const error = await browser.newContext({ viewport: null, isMobile: true }).catch(e => e);
|
||||
expect(error.message).toContain('"isMobile" option is not supported with null "viewport"');
|
||||
});
|
||||
it('close() should work for empty context', async({ browser }) => {
|
||||
const context = await browser.newContext();
|
||||
await context.close();
|
||||
});
|
||||
it('close() should abort waitForEvent', async({ browser }) => {
|
||||
const context = await browser.newContext();
|
||||
const promise = context.waitForEvent('page').catch(e => e);
|
||||
await context.close();
|
||||
let error = await promise;
|
||||
expect(error.message).toContain('Context closed');
|
||||
});
|
||||
it('close() should be callable twice', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
await Promise.all([
|
||||
context.close(),
|
||||
context.close(),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
it('should not report frameless pages on error', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.end(`<a href="${server.EMPTY_PAGE}" target="_blank">Click me</a>`);
|
||||
});
|
||||
let popup;
|
||||
context.on('page', p => popup = p);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.click('"Click me"');
|
||||
await context.close();
|
||||
if (popup) {
|
||||
// This races on Firefox :/
|
||||
expect(popup.isClosed()).toBeTruthy();
|
||||
expect(popup.mainFrame()).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({userAgent})', function() {
|
||||
it('should work', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ userAgent: 'foobar' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should work for subframes', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ userAgent: 'foobar' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
utils.attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should emulate device user-agent', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => navigator.userAgent)).not.toContain('iPhone');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ userAgent: devices['iPhone 6'].userAgent });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should make a copy of default options', async({browser, server}) => {
|
||||
const options = { userAgent: 'foobar' };
|
||||
const context = await browser.newContext(options);
|
||||
options.userAgent = 'wrong';
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({bypassCSP})', function() {
|
||||
it('should bypass CSP meta tag', async({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag.
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
// By-pass CSP and try one more time.
|
||||
{
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass CSP header', async({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag.
|
||||
server.setCSP('/empty.html', 'default-src "self"');
|
||||
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
// By-pass CSP and try one more time.
|
||||
{
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass after cross-process navigation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
});
|
||||
it('should bypass CSP in iframes as well', async({browser, server}) => {
|
||||
// Make sure CSP prohibits addScriptTag in an iframe.
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await frame.evaluate(() => window.__injected)).toBe(undefined);
|
||||
await context.close();
|
||||
}
|
||||
|
||||
// By-pass CSP and try one more time.
|
||||
{
|
||||
const context = await browser.newContext({ bypassCSP: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||
expect(await frame.evaluate(() => window.__injected)).toBe(42);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({javaScriptEnabled})', function() {
|
||||
it('should work', async({browser}) => {
|
||||
{
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
await context.close();
|
||||
}
|
||||
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
expect(await page.evaluate('something')).toBe('forbidden');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should be able to navigate after disabling javascript', async({browser, server}) => {
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.pages()', function() {
|
||||
it('should return all of the pages', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const second = await context.newPage();
|
||||
const allPages = context.pages();
|
||||
expect(allPages.length).toBe(2);
|
||||
expect(allPages).toContain(page);
|
||||
expect(allPages).toContain(second);
|
||||
await context.close();
|
||||
});
|
||||
it('should close all belonging pages once closing context', async function({browser}) {
|
||||
const context = await browser.newContext();
|
||||
await context.newPage();
|
||||
expect(context.pages().length).toBe(1);
|
||||
|
||||
await context.close();
|
||||
expect(context.pages().length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.exposeBinding', () => {
|
||||
it('should work', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
let bindingSource;
|
||||
await context.exposeBinding('add', (source, a, b) => {
|
||||
bindingSource = source;
|
||||
return a + b;
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const result = await page.evaluate(async function() {
|
||||
return add(5, 6);
|
||||
});
|
||||
expect(bindingSource.context).toBe(context);
|
||||
expect(bindingSource.page).toBe(page);
|
||||
expect(bindingSource.frame).toBe(page.mainFrame());
|
||||
expect(result).toEqual(11);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.exposeFunction', () => {
|
||||
it('should work', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.exposeFunction('add', (a, b) => a + b);
|
||||
const page = await context.newPage();
|
||||
await page.exposeFunction('mul', (a, b) => a * b);
|
||||
await context.exposeFunction('sub', (a, b) => a - b);
|
||||
await context.exposeBinding('addHandle', async ({ frame }, a, b) => {
|
||||
const handle = await frame.evaluateHandle(([a, b]) => a + b, [a, b]);
|
||||
return handle;
|
||||
});
|
||||
const result = await page.evaluate(async function() {
|
||||
return { mul: await mul(9, 4), add: await add(9, 4), sub: await sub(9, 4), addHandle: await addHandle(5, 6) };
|
||||
});
|
||||
expect(result).toEqual({ mul: 36, add: 13, sub: 5, addHandle: 11 });
|
||||
await context.close();
|
||||
});
|
||||
it('should throw for duplicate registrations', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.exposeFunction('foo', () => {});
|
||||
await context.exposeFunction('bar', () => {});
|
||||
let error = await context.exposeFunction('foo', () => {}).catch(e => e);
|
||||
expect(error.message).toContain('Function "foo" has been already registered');
|
||||
const page = await context.newPage();
|
||||
error = await page.exposeFunction('foo', () => {}).catch(e => e);
|
||||
expect(error.message).toContain('Function "foo" has been already registered in the browser context');
|
||||
await page.exposeFunction('baz', () => {});
|
||||
error = await context.exposeFunction('baz', () => {}).catch(e => e);
|
||||
expect(error.message).toContain('Function "baz" has been already registered in one of the pages');
|
||||
await context.close();
|
||||
});
|
||||
it('should be callable from-inside addInitScript', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
let args = [];
|
||||
await context.exposeFunction('woof', function(arg) {
|
||||
args.push(arg);
|
||||
});
|
||||
await context.addInitScript(() => woof('context'));
|
||||
const page = await context.newPage();
|
||||
await page.addInitScript(() => woof('page'));
|
||||
args = [];
|
||||
await page.reload();
|
||||
expect(args).toEqual(['context', 'page']);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.route', () => {
|
||||
it('should intercept', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
let intercepted = false;
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted = true;
|
||||
const request = route.request();
|
||||
expect(request.url()).toContain('empty.html');
|
||||
expect(request.headers()['user-agent']).toBeTruthy();
|
||||
expect(request.method()).toBe('GET');
|
||||
expect(request.postData()).toBe(null);
|
||||
expect(request.isNavigationRequest()).toBe(true);
|
||||
expect(request.resourceType()).toBe('document');
|
||||
expect(request.frame() === page.mainFrame()).toBe(true);
|
||||
expect(request.frame().url()).toBe('about:blank');
|
||||
route.continue();
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(intercepted).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should unroute', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
|
||||
let intercepted = [];
|
||||
const handler1 = route => {
|
||||
intercepted.push(1);
|
||||
route.continue();
|
||||
};
|
||||
await context.route('**/empty.html', handler1);
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted.push(2);
|
||||
route.continue();
|
||||
});
|
||||
await context.route('**/empty.html', route => {
|
||||
intercepted.push(3);
|
||||
route.continue();
|
||||
});
|
||||
await context.route('**/*', route => {
|
||||
intercepted.push(4);
|
||||
route.continue();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([1]);
|
||||
|
||||
intercepted = [];
|
||||
await context.unroute('**/empty.html', handler1);
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([2]);
|
||||
|
||||
intercepted = [];
|
||||
await context.unroute('**/empty.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(intercepted).toEqual([4]);
|
||||
|
||||
await context.close();
|
||||
});
|
||||
it('should yield to page.route', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'context' });
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'page' });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(await response.text()).toBe('page');
|
||||
await context.close();
|
||||
});
|
||||
it('should fall back to context.route', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
await context.route('**/empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'context' });
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.route('**/non-empty.html', route => {
|
||||
route.fulfill({ status: 200, body: 'page' });
|
||||
});
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.ok()).toBe(true);
|
||||
expect(await response.text()).toBe('context');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({httpCredentials})', function() {
|
||||
it.fail(CHROMIUM && !HEADLESS)('should fail without credentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
it.fail(CHROMIUM && !HEADLESS)('should work with setHTTPCredentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
let response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.setHTTPCredentials({ username: 'user', password: 'pass' });
|
||||
response = await page.reload();
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
it('should work with correct credentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
it.fail(CHROMIUM && !HEADLESS)('should fail with wrong credentials', async({browser, server}) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'foo', password: 'bar' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
it('should return resource body', async({browser, server}) => {
|
||||
server.setAuth('/playground.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.PREFIX + '/playground.html');
|
||||
expect(response.status()).toBe(200);
|
||||
expect(await page.title()).toBe("Playground");
|
||||
expect((await response.body()).toString()).toContain("Playground");
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.setOffline', function() {
|
||||
it('should work with initial option', async({browser, server}) => {
|
||||
const context = await browser.newContext({offline: true});
|
||||
const page = await context.newPage();
|
||||
let error = null;
|
||||
await page.goto(server.EMPTY_PAGE).catch(e => error = e);
|
||||
expect(error).toBeTruthy();
|
||||
await context.setOffline(false);
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
it('should emulate navigator.onLine', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
|
||||
await context.setOffline(true);
|
||||
expect(await page.evaluate(() => window.navigator.onLine)).toBe(false);
|
||||
await context.setOffline(false);
|
||||
expect(await page.evaluate(() => window.navigator.onLine)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Events.BrowserContext.Page', function() {
|
||||
it('should have url', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
|
||||
]);
|
||||
expect(otherPage.url()).toBe(server.EMPTY_PAGE);
|
||||
await context.close();
|
||||
});
|
||||
it('should have url after domcontentloaded', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.EMPTY_PAGE)
|
||||
]);
|
||||
await otherPage.waitForLoadState('domcontentloaded');
|
||||
expect(otherPage.url()).toBe(server.EMPTY_PAGE);
|
||||
await context.close();
|
||||
});
|
||||
it('should have about:blank url with domcontentloaded', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), 'about:blank')
|
||||
]);
|
||||
await otherPage.waitForLoadState('domcontentloaded');
|
||||
expect(otherPage.url()).toBe('about:blank');
|
||||
await context.close();
|
||||
});
|
||||
it('should have about:blank for empty url with domcontentloaded', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(() => window.open())
|
||||
]);
|
||||
await otherPage.waitForLoadState('domcontentloaded');
|
||||
expect(otherPage.url()).toBe('about:blank');
|
||||
await context.close();
|
||||
});
|
||||
it('should report when a new page is created and closed', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const [otherPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.CROSS_PROCESS_PREFIX + '/empty.html'),
|
||||
]);
|
||||
// The url is about:blank in FF when 'page' event is fired.
|
||||
expect(otherPage.url()).toContain(server.CROSS_PROCESS_PREFIX);
|
||||
expect(await otherPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
|
||||
expect(await otherPage.$('body')).toBeTruthy();
|
||||
|
||||
let allPages = context.pages();
|
||||
expect(allPages).toContain(page);
|
||||
expect(allPages).toContain(otherPage);
|
||||
|
||||
let closeEventReceived;
|
||||
otherPage.once('close', () => closeEventReceived = true);
|
||||
await otherPage.close();
|
||||
expect(closeEventReceived).toBeTruthy();
|
||||
|
||||
allPages = context.pages();
|
||||
expect(allPages).toContain(page);
|
||||
expect(allPages).not.toContain(otherPage);
|
||||
await context.close();
|
||||
});
|
||||
it('should report initialized pages', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const pagePromise = context.waitForEvent('page');
|
||||
context.newPage();
|
||||
const newPage = await pagePromise;
|
||||
expect(newPage.url()).toBe('about:blank');
|
||||
|
||||
const popupPromise = context.waitForEvent('page');
|
||||
const evaluatePromise = newPage.evaluate(() => window.open('about:blank'));
|
||||
const popup = await popupPromise;
|
||||
expect(popup.url()).toBe('about:blank');
|
||||
await evaluatePromise;
|
||||
await context.close();
|
||||
});
|
||||
it('should not crash while redirecting of original request was missed', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
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.
|
||||
const [newPage] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.evaluate(url => window.open(url), server.PREFIX + '/one-style.html'),
|
||||
server.waitForRequest('/one-style.css')
|
||||
]);
|
||||
// Issue a redirect.
|
||||
serverResponse.writeHead(302, { location: '/injectedstyle.css' });
|
||||
serverResponse.end();
|
||||
await newPage.waitForLoadState('domcontentloaded');
|
||||
expect(newPage.url()).toBe(server.PREFIX + '/one-style.html');
|
||||
// Cleanup.
|
||||
await context.close();
|
||||
});
|
||||
it('should have an opener', async({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.goto(server.PREFIX + '/popup/window-open.html')
|
||||
]);
|
||||
expect(popup.url()).toBe(server.PREFIX + '/popup/popup.html');
|
||||
expect(await popup.opener()).toBe(page);
|
||||
expect(await page.opener()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
it('should fire page lifecycle events', async function({browser, server}) {
|
||||
const context = await browser.newContext();
|
||||
const events = [];
|
||||
context.on('page', async page => {
|
||||
events.push('CREATED: ' + page.url());
|
||||
page.on('close', () => events.push('DESTROYED: ' + page.url()));
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.close();
|
||||
expect(events).toEqual([
|
||||
'CREATED: about:blank',
|
||||
`DESTROYED: ${server.EMPTY_PAGE}`
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
it.fail(WEBKIT)('should work with Shift-clicking', async({browser, server}) => {
|
||||
// WebKit: Shift+Click does not open a new window.
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a', { modifiers: ['Shift'] }),
|
||||
]);
|
||||
expect(await popup.opener()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
it.fail(WEBKIT || FFOX)('should work with Ctrl-clicking', async({browser, server}) => {
|
||||
// Firefox: reports an opener in this case.
|
||||
// WebKit: Ctrl+Click does not open a new tab.
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('<a href="/one-style.html">yo</a>');
|
||||
const [popup] = await Promise.all([
|
||||
context.waitForEvent('page'),
|
||||
page.click('a', { modifiers: [ MAC ? 'Meta' : 'Control'] }),
|
||||
]);
|
||||
expect(await popup.opener()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. 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 {FFOX, CHROMIUM, WEBKIT, WIN, LINUX, ASSETS_DIR} = testOptions;
|
||||
|
||||
describe('Capabilities', function() {
|
||||
it.fail(WEBKIT && WIN)('Web Assembly should work', async function({page, server}) {
|
||||
await page.goto(server.PREFIX + '/wasm/table2.html');
|
||||
expect(await page.evaluate(() => loadTable())).toBe('42, 83');
|
||||
});
|
||||
|
||||
it('WebSocket should work', async({page, server}) => {
|
||||
const value = await page.evaluate((port) => {
|
||||
let cb;
|
||||
const result = new Promise(f => cb = f);
|
||||
const ws = new WebSocket('ws://localhost:' + port + '/ws');
|
||||
ws.addEventListener('message', data => { ws.close(); cb(data.data); });
|
||||
ws.addEventListener('error', error => cb('Error'));
|
||||
return result;
|
||||
}, server.PORT);
|
||||
expect(value).toBe('incoming');
|
||||
});
|
||||
|
||||
it('should respect CSP', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
res.setHeader('Content-Security-Policy', `script-src 'unsafe-inline';`);
|
||||
res.end(`
|
||||
<script>
|
||||
window.testStatus = 'SUCCESS';
|
||||
window.testStatus = eval("'FAILED'");
|
||||
</script>`);
|
||||
});
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => window.testStatus)).toBe('SUCCESS');
|
||||
});
|
||||
it.fail(WEBKIT && WIN)('should play video', async({page}) => {
|
||||
// TODO: the test passes on Windows locally but fails on GitHub Action bot,
|
||||
// apparently due to a Media Pack issue in the Windows Server.
|
||||
//
|
||||
// Our test server doesn't support range requests required to play on Mac,
|
||||
// so we load the page using a file url.
|
||||
const url = WIN
|
||||
? 'file:///' + path.join(ASSETS_DIR, 'video.html').replace(/\\/g, '/')
|
||||
: 'file://' + path.join(ASSETS_DIR, 'video.html');
|
||||
await page.goto(url);
|
||||
await page.$eval('video', v => v.play());
|
||||
await page.$eval('video', v => v.pause());
|
||||
});
|
||||
});
|
||||
64
test/capabilities.spec.js
Normal file
64
test/capabilities.spec.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. 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 {FFOX, CHROMIUM, WEBKIT, WIN, LINUX, ASSETS_DIR} = testOptions;
|
||||
|
||||
it.fail(WEBKIT && WIN)('Web Assembly should work', async function({page, server}) {
|
||||
await page.goto(server.PREFIX + '/wasm/table2.html');
|
||||
expect(await page.evaluate(() => loadTable())).toBe('42, 83');
|
||||
});
|
||||
|
||||
it('WebSocket should work', async({page, server}) => {
|
||||
const value = await page.evaluate((port) => {
|
||||
let cb;
|
||||
const result = new Promise(f => cb = f);
|
||||
const ws = new WebSocket('ws://localhost:' + port + '/ws');
|
||||
ws.addEventListener('message', data => { ws.close(); cb(data.data); });
|
||||
ws.addEventListener('error', error => cb('Error'));
|
||||
return result;
|
||||
}, server.PORT);
|
||||
expect(value).toBe('incoming');
|
||||
});
|
||||
|
||||
it('should respect CSP', async({page, server}) => {
|
||||
server.setRoute('/empty.html', async (req, res) => {
|
||||
res.setHeader('Content-Security-Policy', `script-src 'unsafe-inline';`);
|
||||
res.end(`
|
||||
<script>
|
||||
window.testStatus = 'SUCCESS';
|
||||
window.testStatus = eval("'FAILED'");
|
||||
</script>`);
|
||||
});
|
||||
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => window.testStatus)).toBe('SUCCESS');
|
||||
});
|
||||
|
||||
it.fail(WEBKIT && WIN)('should play video', async({page}) => {
|
||||
// TODO: the test passes on Windows locally but fails on GitHub Action bot,
|
||||
// apparently due to a Media Pack issue in the Windows Server.
|
||||
//
|
||||
// Our test server doesn't support range requests required to play on Mac,
|
||||
// so we load the page using a file url.
|
||||
const url = WIN
|
||||
? 'file:///' + path.join(ASSETS_DIR, 'video.html').replace(/\\/g, '/')
|
||||
: 'file://' + path.join(ASSETS_DIR, 'video.html');
|
||||
await page.goto(url);
|
||||
await page.$eval('video', v => v.play());
|
||||
await page.$eval('video', v => v.pause());
|
||||
});
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 { FFOX, CHROMIUM, WEBKIT, WIN, CHANNEL } = testOptions;
|
||||
|
||||
describe.skip(!CHANNEL)('Channels', function() {
|
||||
it('should work', async({browser}) => {
|
||||
expect(!!browser._connection).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should scope context handles', async({browserType, browser, server}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browser, GOLDEN_PRECONDITION);
|
||||
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await expectScopeState(browser, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'BrowserContext', objects: [
|
||||
{ _guid: 'Frame', objects: [] },
|
||||
{ _guid: 'Page', objects: [] },
|
||||
{ _guid: 'Request', objects: [] },
|
||||
{ _guid: 'Response', objects: [] },
|
||||
]},
|
||||
] },
|
||||
] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
await context.close();
|
||||
await expectScopeState(browser, GOLDEN_PRECONDITION);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should scope CDPSession handles', async({browserType, browser, server}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
|
||||
const session = await browser.newBrowserCDPSession();
|
||||
await expectScopeState(browserType, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'CDPSession', objects: [] },
|
||||
] },
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
await session.detach();
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
});
|
||||
|
||||
it('should scope browser handles', async({browserType, defaultBrowserOptions}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
await browser.newContext();
|
||||
await expectScopeState(browserType, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'BrowserContext', objects: [] }
|
||||
] },
|
||||
{ _guid: 'Browser', objects: [] },
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
await browser.close();
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
});
|
||||
});
|
||||
|
||||
async function expectScopeState(object, golden) {
|
||||
golden = trimGuids(golden);
|
||||
const remoteState = trimGuids(await object._channel.debugScopeState());
|
||||
const localState = trimGuids(object._connection._debugScopeState());
|
||||
expect(localState).toEqual(golden);
|
||||
expect(remoteState).toEqual(golden);
|
||||
}
|
||||
|
||||
function compareObjects(a, b) {
|
||||
if (a._guid !== b._guid)
|
||||
return a._guid.localeCompare(b._guid);
|
||||
return a.objects.length - b.objects.length;
|
||||
}
|
||||
|
||||
function trimGuids(object) {
|
||||
if (Array.isArray(object))
|
||||
return object.map(trimGuids).sort(compareObjects);
|
||||
if (typeof object === 'object') {
|
||||
const result = {};
|
||||
for (const key in object)
|
||||
result[key] = trimGuids(object[key]);
|
||||
return result;
|
||||
}
|
||||
if (typeof object === 'string')
|
||||
return object ? object.match(/[^@]+/)[0] : '';
|
||||
return object;
|
||||
}
|
||||
170
test/channels.spec.js
Normal file
170
test/channels.spec.js
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 { FFOX, CHROMIUM, WEBKIT, WIN, CHANNEL } = testOptions;
|
||||
|
||||
it.skip(!CHANNEL)('should work', async({browser}) => {
|
||||
expect(!!browser._connection).toBeTruthy();
|
||||
});
|
||||
|
||||
it.skip(!CHANNEL)('should scope context handles', async({browserType, browser, server}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browser, GOLDEN_PRECONDITION);
|
||||
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await expectScopeState(browser, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'BrowserContext', objects: [
|
||||
{ _guid: 'Frame', objects: [] },
|
||||
{ _guid: 'Page', objects: [] },
|
||||
{ _guid: 'Request', objects: [] },
|
||||
{ _guid: 'Response', objects: [] },
|
||||
]},
|
||||
] },
|
||||
] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
await context.close();
|
||||
await expectScopeState(browser, GOLDEN_PRECONDITION);
|
||||
});
|
||||
|
||||
it.skip(!CHANNEL || !CHROMIUM)('should scope CDPSession handles', async({browserType, browser, server}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
|
||||
const session = await browser.newBrowserCDPSession();
|
||||
await expectScopeState(browserType, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'CDPSession', objects: [] },
|
||||
] },
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
await session.detach();
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
});
|
||||
|
||||
it.skip(!CHANNEL)('should scope browser handles', async({browserType, defaultBrowserOptions}) => {
|
||||
const GOLDEN_PRECONDITION = {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [] }
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
};
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
await browser.newContext();
|
||||
await expectScopeState(browserType, {
|
||||
_guid: '',
|
||||
objects: [
|
||||
{ _guid: 'BrowserType', objects: [
|
||||
{ _guid: 'Browser', objects: [
|
||||
{ _guid: 'BrowserContext', objects: [] }
|
||||
] },
|
||||
{ _guid: 'Browser', objects: [] },
|
||||
] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'BrowserType', objects: [] },
|
||||
{ _guid: 'Playwright', objects: [] },
|
||||
{ _guid: 'Selectors', objects: [] },
|
||||
{ _guid: 'Electron', objects: [] },
|
||||
]
|
||||
});
|
||||
|
||||
await browser.close();
|
||||
await expectScopeState(browserType, GOLDEN_PRECONDITION);
|
||||
});
|
||||
|
||||
async function expectScopeState(object, golden) {
|
||||
golden = trimGuids(golden);
|
||||
const remoteState = trimGuids(await object._channel.debugScopeState());
|
||||
const localState = trimGuids(object._connection._debugScopeState());
|
||||
expect(localState).toEqual(golden);
|
||||
expect(remoteState).toEqual(golden);
|
||||
}
|
||||
|
||||
function compareObjects(a, b) {
|
||||
if (a._guid !== b._guid)
|
||||
return a._guid.localeCompare(b._guid);
|
||||
return a.objects.length - b.objects.length;
|
||||
}
|
||||
|
||||
function trimGuids(object) {
|
||||
if (Array.isArray(object))
|
||||
return object.map(trimGuids).sort(compareObjects);
|
||||
if (typeof object === 'object') {
|
||||
const result = {};
|
||||
for (const key in object)
|
||||
result[key] = trimGuids(object[key]);
|
||||
return result;
|
||||
}
|
||||
if (typeof object === 'string')
|
||||
return object ? object.match(/[^@]+/)[0] : '';
|
||||
return object;
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
describe('Page.check', function() {
|
||||
it('should check the box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
await page.check('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
it('should not check the checked box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
|
||||
await page.check('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
it('should uncheck the box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
|
||||
await page.uncheck('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(false);
|
||||
});
|
||||
it('should not uncheck the unchecked box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
await page.uncheck('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(false);
|
||||
});
|
||||
it('should check the box by label', async({page}) => {
|
||||
await page.setContent(`<label for='checkbox'><input id='checkbox' type='checkbox'></input></label>`);
|
||||
await page.check('label');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
it('should check the box outside label', async({page}) => {
|
||||
await page.setContent(`<label for='checkbox'>Text</label><div><input id='checkbox' type='checkbox'></input></div>`);
|
||||
await page.check('label');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
it('should check the box inside label w/o id', async({page}) => {
|
||||
await page.setContent(`<label>Text<span><input id='checkbox' type='checkbox'></input></span></label>`);
|
||||
await page.check('label');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
it('should check radio', async({page}) => {
|
||||
await page.setContent(`
|
||||
<input type='radio'>one</input>
|
||||
<input id='two' type='radio'>two</input>
|
||||
<input type='radio'>three</input>`);
|
||||
await page.check('#two');
|
||||
expect(await page.evaluate(() => two.checked)).toBe(true);
|
||||
});
|
||||
it('should check the box by aria role', async({page}) => {
|
||||
await page.setContent(`<div role='checkbox' id='checkbox'>CHECKBOX</div>
|
||||
<script>
|
||||
checkbox.addEventListener('click', () => checkbox.setAttribute('aria-checked', 'true'));
|
||||
</script>`);
|
||||
await page.check('div');
|
||||
expect(await page.evaluate(() => checkbox.getAttribute('aria-checked'))).toBe('true');
|
||||
});
|
||||
});
|
||||
76
test/check.spec.js
Normal file
76
test/check.spec.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
it('should check the box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
await page.check('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not check the checked box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
|
||||
await page.check('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should uncheck the box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
|
||||
await page.uncheck('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not uncheck the unchecked box', async({page}) => {
|
||||
await page.setContent(`<input id='checkbox' type='checkbox'></input>`);
|
||||
await page.uncheck('input');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(false);
|
||||
});
|
||||
|
||||
it('should check the box by label', async({page}) => {
|
||||
await page.setContent(`<label for='checkbox'><input id='checkbox' type='checkbox'></input></label>`);
|
||||
await page.check('label');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check the box outside label', async({page}) => {
|
||||
await page.setContent(`<label for='checkbox'>Text</label><div><input id='checkbox' type='checkbox'></input></div>`);
|
||||
await page.check('label');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check the box inside label w/o id', async({page}) => {
|
||||
await page.setContent(`<label>Text<span><input id='checkbox' type='checkbox'></input></span></label>`);
|
||||
await page.check('label');
|
||||
expect(await page.evaluate(() => checkbox.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check radio', async({page}) => {
|
||||
await page.setContent(`
|
||||
<input type='radio'>one</input>
|
||||
<input id='two' type='radio'>two</input>
|
||||
<input type='radio'>three</input>`);
|
||||
await page.check('#two');
|
||||
expect(await page.evaluate(() => two.checked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should check the box by aria role', async({page}) => {
|
||||
await page.setContent(`<div role='checkbox' id='checkbox'>CHECKBOX</div>
|
||||
<script>
|
||||
checkbox.addEventListener('click', () => checkbox.setAttribute('aria-checked', 'true'));
|
||||
</script>`);
|
||||
await page.check('div');
|
||||
expect(await page.evaluate(() => checkbox.getAttribute('aria-checked'))).toBe('true');
|
||||
});
|
||||
117
test/chromium-css-coverage.spec.js
Normal file
117
test/chromium-css-coverage.spec.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* Copyright 2018 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 {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
it.skip(!CHROMIUM)('should work', async function({browserType, page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/simple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/simple.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 1, end: 22}
|
||||
]);
|
||||
const range = coverage[0].ranges[0];
|
||||
expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }');
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should report sourceURLs', async function({page, server}) {
|
||||
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.skip(!CHROMIUM)('should report multiple stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css');
|
||||
expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css');
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should report stylesheets that have no coverage', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/unused.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('unused.css');
|
||||
expect(coverage[0].ranges.length).toBe(0);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should work with media queries', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/media.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/media.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 17, end: 38}
|
||||
]);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should work with complicated usecases', async function({page, server}) {
|
||||
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>/')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should ignore injected stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.addStyleTag({content: 'body { margin: 10px;}'});
|
||||
// trigger style recalc
|
||||
const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin);
|
||||
expect(margin).toBe('10px');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should report stylesheets across navigations', async function({page, server}) {
|
||||
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.skip(!CHROMIUM)('should NOT report scripts across navigations', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should work with a recently loaded stylesheet', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.evaluate(async url => {
|
||||
document.body.textContent = 'hello, world';
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
document.head.appendChild(link);
|
||||
await new Promise(x => link.onload = x);
|
||||
await new Promise(f => requestAnimationFrame(f));
|
||||
}, server.PREFIX + '/csscoverage/stylesheet1.css');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
97
test/chromium-js-coverage.spec.js
Normal file
97
test/chromium-js-coverage.spec.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* Copyright 2018 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 {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
it.skip(CHROMIUM)('should be missing', async function({page, server}) {
|
||||
expect(page.coverage).toBe(null);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should work', async function({browserType, page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should report sourceURLs', async function({page, server}) {
|
||||
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.skip(!CHROMIUM)('should ignore eval() scripts by default', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/eval.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
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.skip(!CHROMIUM)('should ignore playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({reportAnonymousScripts: true});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate('console.log("foo")');
|
||||
await page.evaluate(() => console.log('bar'));
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should report multiple scripts', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/jscoverage/script1.js');
|
||||
expect(coverage[1].url).toContain('/jscoverage/script2.js');
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should report scripts across navigations when disabled', async function({page, server}) {
|
||||
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.skip(!CHROMIUM)('should NOT report scripts across navigations when enabled', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('should not hang when there is a debugger statement', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
});
|
||||
await page.coverage.stopJSCoverage();
|
||||
});
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Page.click', function() {
|
||||
it.skip(USES_HOOKS)('should avoid side effects after timeout', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const error = await page.click('button', { timeout: 2000, __testHookBeforePointerAction: () => new Promise(f => setTimeout(f, 2500))}).catch(e => e);
|
||||
await page.waitForTimeout(5000); // Give it some time to click after the test hook is done waiting.
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
expect(error.message).toContain('page.click: Timeout 2000ms exceeded.');
|
||||
});
|
||||
it('should timeout waiting for button to be enabled', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;" disabled><span>Click target</span></button>');
|
||||
const error = await page.click('text=Click target', { timeout: 3000 }).catch(e => e);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(error.message).toContain('page.click: Timeout 3000ms exceeded.');
|
||||
expect(error.message).toContain('element is disabled - waiting');
|
||||
});
|
||||
});
|
||||
34
test/click-timeout-1.spec.js
Normal file
34
test/click-timeout-1.spec.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
it.skip(USES_HOOKS)('should avoid side effects after timeout', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const error = await page.click('button', { timeout: 2000, __testHookBeforePointerAction: () => new Promise(f => setTimeout(f, 2500))}).catch(e => e);
|
||||
await page.waitForTimeout(5000); // Give it some time to click after the test hook is done waiting.
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
expect(error.message).toContain('page.click: Timeout 2000ms exceeded.');
|
||||
});
|
||||
|
||||
it('should timeout waiting for button to be enabled', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;" disabled><span>Click target</span></button>');
|
||||
const error = await page.click('text=Click target', { timeout: 3000 }).catch(e => e);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(error.message).toContain('page.click: Timeout 3000ms exceeded.');
|
||||
expect(error.message).toContain('element is disabled - waiting');
|
||||
});
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Page.click', function() {
|
||||
it('should timeout waiting for display:none to be gone', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
const error = await page.click('button', { timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for element to be visible, enabled and not moving');
|
||||
expect(error.message).toContain('element is not visible - waiting');
|
||||
});
|
||||
it('should timeout waiting for visbility:hidden to be gone', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.visibility = 'hidden');
|
||||
const error = await page.click('button', { timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for element to be visible, enabled and not moving');
|
||||
expect(error.message).toContain('element is not visible - waiting');
|
||||
});
|
||||
});
|
||||
36
test/click-timeout-2.spec.js
Normal file
36
test/click-timeout-2.spec.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
it('should timeout waiting for display:none to be gone', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
const error = await page.click('button', { timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for element to be visible, enabled and not moving');
|
||||
expect(error.message).toContain('element is not visible - waiting');
|
||||
});
|
||||
|
||||
it('should timeout waiting for visbility:hidden to be gone', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.visibility = 'hidden');
|
||||
const error = await page.click('button', { timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for element to be visible, enabled and not moving');
|
||||
expect(error.message).toContain('element is not visible - waiting');
|
||||
});
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Page.click', function() {
|
||||
it.skip(USES_HOOKS)('should fail when element jumps during hit testing', async({page, server}) => {
|
||||
await page.setContent('<button>Click me</button>');
|
||||
let clicked = false;
|
||||
const handle = await page.$('button');
|
||||
const __testHookBeforeHitTarget = () => page.evaluate(() => {
|
||||
const margin = parseInt(document.querySelector('button').style.marginLeft || 0) + 100;
|
||||
document.querySelector('button').style.marginLeft = margin + 'px';
|
||||
});
|
||||
const promise = handle.click({ timeout: 5000, __testHookBeforeHitTarget }).then(() => clicked = true).catch(e => e);
|
||||
const error = await promise;
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('element does not receive pointer events');
|
||||
expect(error.message).toContain('retrying click action');
|
||||
});
|
||||
it('should timeout waiting for hit target', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(() => {
|
||||
document.body.style.position = 'relative';
|
||||
const blocker = document.createElement('div');
|
||||
blocker.style.position = 'absolute';
|
||||
blocker.style.width = '400px';
|
||||
blocker.style.height = '20px';
|
||||
blocker.style.left = '0';
|
||||
blocker.style.top = '0';
|
||||
document.body.appendChild(blocker);
|
||||
});
|
||||
const error = await button.click({ timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('element does not receive pointer events');
|
||||
expect(error.message).toContain('retrying click action');
|
||||
});
|
||||
});
|
||||
54
test/click-timeout-3.spec.js
Normal file
54
test/click-timeout-3.spec.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
it.skip(USES_HOOKS)('should fail when element jumps during hit testing', async({page, server}) => {
|
||||
await page.setContent('<button>Click me</button>');
|
||||
let clicked = false;
|
||||
const handle = await page.$('button');
|
||||
const __testHookBeforeHitTarget = () => page.evaluate(() => {
|
||||
const margin = parseInt(document.querySelector('button').style.marginLeft || 0) + 100;
|
||||
document.querySelector('button').style.marginLeft = margin + 'px';
|
||||
});
|
||||
const promise = handle.click({ timeout: 5000, __testHookBeforeHitTarget }).then(() => clicked = true).catch(e => e);
|
||||
const error = await promise;
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('element does not receive pointer events');
|
||||
expect(error.message).toContain('retrying click action');
|
||||
});
|
||||
|
||||
it('should timeout waiting for hit target', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(() => {
|
||||
document.body.style.position = 'relative';
|
||||
const blocker = document.createElement('div');
|
||||
blocker.style.position = 'absolute';
|
||||
blocker.style.width = '400px';
|
||||
blocker.style.height = '20px';
|
||||
blocker.style.left = '0';
|
||||
blocker.style.top = '0';
|
||||
document.body.appendChild(blocker);
|
||||
});
|
||||
const error = await button.click({ timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('element does not receive pointer events');
|
||||
expect(error.message).toContain('retrying click action');
|
||||
});
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Page.click', function() {
|
||||
it('should timeout waiting for stable position', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.evaluate(button => {
|
||||
button.style.transition = 'margin 5s linear 0s';
|
||||
button.style.marginLeft = '200px';
|
||||
});
|
||||
const error = await button.click({ timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for element to be visible, enabled and not moving');
|
||||
expect(error.message).toContain('element is moving - waiting');
|
||||
});
|
||||
});
|
||||
29
test/click-timeout-4.spec.js
Normal file
29
test/click-timeout-4.spec.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
it('should timeout waiting for stable position', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.evaluate(button => {
|
||||
button.style.transition = 'margin 5s linear 0s';
|
||||
button.style.marginLeft = '200px';
|
||||
});
|
||||
const error = await button.click({ timeout: 5000 }).catch(e => e);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 5000ms exceeded.');
|
||||
expect(error.message).toContain('waiting for element to be visible, enabled and not moving');
|
||||
expect(error.message).toContain('element is moving - waiting');
|
||||
});
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Page.click', function() {
|
||||
it.fail(true)('should report that selector does not match anymore', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
const __testHookAfterStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));
|
||||
else
|
||||
renderComponent(e('div', {}, []));
|
||||
});
|
||||
const error = await page.dblclick('text=button1', { __testHookAfterStable, timeout: 3000 }).catch(e => e);
|
||||
expect(await page.evaluate(() => window.button1)).toBe(undefined);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
expect(error.message).toContain('page.dblclick: Timeout 3000ms exceeded.');
|
||||
expect(error.message).toContain('element does not match the selector anymore');
|
||||
});
|
||||
it.fail(true)('should not retarget the handle when element is recycled', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] ));
|
||||
});
|
||||
const __testHookBeforeStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] ));
|
||||
});
|
||||
const handle = await page.$('text=button1');
|
||||
const error = await handle.click({ __testHookBeforeStable, timeout: 3000 }).catch(e => e);
|
||||
expect(await page.evaluate(() => window.button1)).toBe(undefined);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 3000ms exceeded.');
|
||||
expect(error.message).toContain('element is disabled - waiting');
|
||||
});
|
||||
it('should timeout when click opens alert', async({page, server}) => {
|
||||
const dialogPromise = page.waitForEvent('dialog');
|
||||
await page.setContent(`<div onclick='window.alert(123)'>Click me</div>`);
|
||||
const error = await page.click('div', { timeout: 3000 }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 3000ms exceeded.');
|
||||
const dialog = await dialogPromise;
|
||||
await dialog.dismiss();
|
||||
});
|
||||
});
|
||||
62
test/click-timeout-5.spec.js
Normal file
62
test/click-timeout-5.spec.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
it.fail(true)('should report that selector does not match anymore', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
const __testHookAfterStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));
|
||||
else
|
||||
renderComponent(e('div', {}, []));
|
||||
});
|
||||
const error = await page.dblclick('text=button1', { __testHookAfterStable, timeout: 3000 }).catch(e => e);
|
||||
expect(await page.evaluate(() => window.button1)).toBe(undefined);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
expect(error.message).toContain('page.dblclick: Timeout 3000ms exceeded.');
|
||||
expect(error.message).toContain('element does not match the selector anymore');
|
||||
});
|
||||
|
||||
it.fail(true)('should not retarget the handle when element is recycled', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] ));
|
||||
});
|
||||
const __testHookBeforeStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] ));
|
||||
});
|
||||
const handle = await page.$('text=button1');
|
||||
const error = await handle.click({ __testHookBeforeStable, timeout: 3000 }).catch(e => e);
|
||||
expect(await page.evaluate(() => window.button1)).toBe(undefined);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
expect(error.message).toContain('elementHandle.click: Timeout 3000ms exceeded.');
|
||||
expect(error.message).toContain('element is disabled - waiting');
|
||||
});
|
||||
|
||||
it('should timeout when click opens alert', async({page, server}) => {
|
||||
const dialogPromise = page.waitForEvent('dialog');
|
||||
await page.setContent(`<div onclick='window.alert(123)'>Click me</div>`);
|
||||
const error = await page.click('div', { timeout: 3000 }).catch(e => e);
|
||||
expect(error.message).toContain('page.click: Timeout 3000ms exceeded.');
|
||||
const dialog = await dialogPromise;
|
||||
await dialog.dismiss();
|
||||
});
|
||||
|
|
@ -1,728 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, HEADLESS, USES_HOOKS} = testOptions;
|
||||
|
||||
async function giveItAChanceToClick(page) {
|
||||
for (let i = 0; i < 5; i++)
|
||||
await page.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
|
||||
}
|
||||
|
||||
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('should click svg', async({page, server}) => {
|
||||
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('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/GoogleChrome/puppeteer/issues/4281
|
||||
it('should click on a span with an inline element inside', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
span::before {
|
||||
content: 'q';
|
||||
}
|
||||
</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({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await Promise.all([
|
||||
page.close(),
|
||||
page.mouse.click(1, 2),
|
||||
]).catch(e => {});
|
||||
await context.close();
|
||||
});
|
||||
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('should click the button after a cross origin navigation ', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/input/button.html');
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should click with disabled javascript', async({browser, server}) => {
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/wrappedlink.html');
|
||||
await Promise.all([
|
||||
page.click('a'),
|
||||
page.waitForNavigation()
|
||||
]);
|
||||
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked');
|
||||
await context.close();
|
||||
});
|
||||
it('should click when one of inline box children is outside of viewport', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
i {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
}
|
||||
</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');
|
||||
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
||||
await page.fill('textarea', text);
|
||||
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('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 waitFor visible when already visible', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should not wait with force', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
await page.click('button', { force: true }).catch(e => error = e);
|
||||
expect(error.message).toContain('Element is not visible');
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
});
|
||||
it('should waitFor display:none to be gone', async({page, server}) => {
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
const clicked = page.click('button', { timeout: 0 }).then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('button', b => b.style.display = 'block');
|
||||
await clicked;
|
||||
expect(done).toBe(true);
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should waitFor visibility:hidden to be gone', async({page, server}) => {
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.visibility = 'hidden');
|
||||
const clicked = page.click('button', { timeout: 0 }).then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('button', b => b.style.visibility = 'visible');
|
||||
await clicked;
|
||||
expect(done).toBe(true);
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should waitFor visible when parent is hidden', async({page, server}) => {
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.parentElement.style.display = 'none');
|
||||
const clicked = page.click('button', { timeout: 0 }).then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('button', b => b.parentElement.style.display = 'block');
|
||||
await clicked;
|
||||
expect(done).toBe(true);
|
||||
expect(await page.evaluate(() => result)).toBe('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('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 not hang with touch-enabled viewports', async({browser, playwright}) => {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/161
|
||||
const { viewport, hasTouch } = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ viewport, hasTouch });
|
||||
const page = await context.newPage();
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(100, 10);
|
||||
await page.mouse.up();
|
||||
await context.close();
|
||||
});
|
||||
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('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;
|
||||
});
|
||||
});
|
||||
await page.dblclick('button');
|
||||
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/GoogleChrome/puppeteer/issues/206
|
||||
it('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('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/GoogleChrome/puppeteer/issues/4110
|
||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390
|
||||
// @see https://chromium-review.googlesource.com/c/chromium/src/+/1742784
|
||||
it.fail(CHROMIUM || WEBKIT)('should click the button with fixed position inside an iframe', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setViewportSize({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('should click the button with deviceScaleFactor set', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 400, height: 400 }, deviceScaleFactor: 5 });
|
||||
const page = await context.newPage();
|
||||
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 context.close();
|
||||
});
|
||||
it('should click the button with px border with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '8px');
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 20 + 8 : 20);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 10 + 8 : 10);
|
||||
});
|
||||
it('should click the button with em border with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '2em');
|
||||
await page.$eval('button', button => button.style.fontSize = '12px');
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 12 * 2 + 20 : 20);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 12 * 2 + 10 : 10);
|
||||
});
|
||||
it('should click a very large button with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '8px');
|
||||
await page.$eval('button', button => button.style.height = button.style.width = '2000px');
|
||||
await page.click('button', { position: { x: 1900, y: 1910 } });
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910);
|
||||
});
|
||||
it('should click a button in scrolling container with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
const container = document.createElement('div');
|
||||
container.style.overflow = 'auto';
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
button.parentElement.insertBefore(container, button);
|
||||
container.appendChild(button);
|
||||
button.style.height = '2000px';
|
||||
button.style.width = '2000px';
|
||||
button.style.borderWidth = '8px';
|
||||
});
|
||||
await page.click('button', { position: { x: 1900, y: 1910 } });
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910);
|
||||
});
|
||||
it.skip(FFOX)('should click the button with offset with page scale', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
button.style.borderWidth = '8px';
|
||||
document.body.style.margin = '0';
|
||||
});
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
const round = x => Math.round(x + 0.01);
|
||||
let expected = { x: 28, y: 18 }; // 20;10 + 8px of border in each direction
|
||||
if (WEBKIT) {
|
||||
// WebKit rounds up during css -> dip -> css conversion.
|
||||
expected = { x: 29, y: 19 };
|
||||
} else if (CHROMIUM && HEADLESS) {
|
||||
// Headless Chromium rounds down during css -> dip -> css conversion.
|
||||
expected = { x: 27, y: 18 };
|
||||
}
|
||||
expect(round(await page.evaluate(() => pageX))).toBe(expected.x);
|
||||
expect(round(await page.evaluate(() => pageY))).toBe(expected.y);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should wait for stable position', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
button.style.transition = 'margin 500ms linear 0s';
|
||||
button.style.marginLeft = '200px';
|
||||
button.style.borderWidth = '0';
|
||||
button.style.width = '200px';
|
||||
button.style.height = '20px';
|
||||
// Set display to "block" - otherwise Firefox layouts with non-even
|
||||
// values on Linux.
|
||||
button.style.display = 'block';
|
||||
document.body.style.margin = '0';
|
||||
});
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
expect(await page.evaluate(() => pageX)).toBe(300);
|
||||
expect(await page.evaluate(() => pageY)).toBe(10);
|
||||
});
|
||||
it('should wait for becoming hit target', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
button.style.borderWidth = '0';
|
||||
button.style.width = '200px';
|
||||
button.style.height = '20px';
|
||||
document.body.style.margin = '0';
|
||||
document.body.style.position = 'relative';
|
||||
const flyOver = document.createElement('div');
|
||||
flyOver.className = 'flyover';
|
||||
flyOver.style.position = 'absolute';
|
||||
flyOver.style.width = '400px';
|
||||
flyOver.style.height = '20px';
|
||||
flyOver.style.left = '-200px';
|
||||
flyOver.style.top = '0';
|
||||
flyOver.style.background = 'red';
|
||||
document.body.appendChild(flyOver);
|
||||
});
|
||||
let clicked = false;
|
||||
const clickPromise = page.click('button').then(() => clicked = true);
|
||||
expect(clicked).toBe(false);
|
||||
|
||||
await page.$eval('.flyover', flyOver => flyOver.style.left = '0');
|
||||
await giveItAChanceToClick(page);
|
||||
expect(clicked).toBe(false);
|
||||
|
||||
await page.$eval('.flyover', flyOver => flyOver.style.left = '200px');
|
||||
await clickPromise;
|
||||
expect(clicked).toBe(true);
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
});
|
||||
it('should fail when obscured and not waiting for hit target', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(() => {
|
||||
document.body.style.position = 'relative';
|
||||
const blocker = document.createElement('div');
|
||||
blocker.style.position = 'absolute';
|
||||
blocker.style.width = '400px';
|
||||
blocker.style.height = '20px';
|
||||
blocker.style.left = '0';
|
||||
blocker.style.top = '0';
|
||||
document.body.appendChild(blocker);
|
||||
});
|
||||
await button.click({ force: true });
|
||||
expect(await page.evaluate(() => window.result)).toBe('Was not clicked');
|
||||
});
|
||||
|
||||
it('should wait for button to be enabled', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;" disabled><span>Click target</span></button>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('text=Click target').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('button').removeAttribute('disabled'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should wait for input to be enabled', async({page, server}) => {
|
||||
await page.setContent('<input onclick="javascript:window.__CLICKED=true;" disabled>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('input').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('input').removeAttribute('disabled'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should wait for select to be enabled', async({page, server}) => {
|
||||
await page.setContent('<select onclick="javascript:window.__CLICKED=true;" disabled><option selected>Hello</option></select>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('select').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('select').removeAttribute('disabled'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should click disabled div', async({page, server}) => {
|
||||
await page.setContent('<div onclick="javascript:window.__CLICKED=true;" disabled>Click target</div>');
|
||||
await page.click('text=Click target');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should climb dom for inner label with pointer-events:none', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;"><label style="pointer-events:none">Click target</label></button>');
|
||||
await page.click('text=Click target');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should climb up to [role=button]', async({page, server}) => {
|
||||
await page.setContent('<div role=button onclick="javascript:window.__CLICKED=true;"><div style="pointer-events:none"><span><div>Click target</div></span></div>');
|
||||
await page.click('text=Click target');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should wait for BUTTON to be clickable when it has pointer-events:none', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;" style="pointer-events:none"><span>Click target</span></button>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('text=Click target').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('button').style.removeProperty('pointer-events'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should wait for LABEL to be clickable when it has pointer-events:none', async({page, server}) => {
|
||||
await page.setContent('<label onclick="javascript:window.__CLICKED=true;" style="pointer-events:none"><span>Click target</span></label>');
|
||||
const clickPromise = page.click('text=Click target');
|
||||
// Do a few roundtrips to the page.
|
||||
for (let i = 0; i < 5; ++i)
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
// remove `pointer-events: none` css from button.
|
||||
await page.evaluate(() => document.querySelector('label').style.removeProperty('pointer-events'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
it('should update modifiers correctly', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button', { modifiers: ['Shift'] });
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(true);
|
||||
await page.click('button', { modifiers: [] });
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(false);
|
||||
|
||||
await page.keyboard.down('Shift');
|
||||
await page.click('button', { modifiers: [] });
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(false);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(true);
|
||||
await page.keyboard.up('Shift');
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(false);
|
||||
});
|
||||
it('should click an offscreen element when scroll-behavior is smooth', async({page}) => {
|
||||
await page.setContent(`
|
||||
<div style="border: 1px solid black; height: 500px; overflow: auto; width: 500px; scroll-behavior: smooth">
|
||||
<button style="margin-top: 2000px" onClick="window.clicked = true">hi</button>
|
||||
</div>
|
||||
`);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate('window.clicked')).toBe(true);
|
||||
});
|
||||
it('should report nice error when element is detached and force-clicked', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/animating-button.html');
|
||||
await page.evaluate(() => addButton());
|
||||
const handle = await page.$('button');
|
||||
await page.evaluate(() => stopButton(true));
|
||||
const promise = handle.click({ force: true }).catch(e => e);
|
||||
const error = await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
it('should fail when element detaches after animation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/animating-button.html');
|
||||
await page.evaluate(() => addButton());
|
||||
const handle = await page.$('button');
|
||||
const promise = handle.click().catch(e => e);
|
||||
await page.evaluate(() => stopButton(true));
|
||||
const error = await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
it('should retry when element detaches after animation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/animating-button.html');
|
||||
await page.evaluate(() => addButton());
|
||||
let clicked = false;
|
||||
const promise = page.click('button').then(() => clicked = true);
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
await page.evaluate(() => stopButton(true));
|
||||
await page.evaluate(() => addButton());
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
await page.evaluate(() => stopButton(true));
|
||||
await page.evaluate(() => addButton());
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
await page.evaluate(() => stopButton(false));
|
||||
await promise;
|
||||
expect(clicked).toBe(true);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(true);
|
||||
});
|
||||
it('should retry when element is animating from outside the viewport', async({page, server}) => {
|
||||
await page.setContent(`<style>
|
||||
@keyframes move {
|
||||
from { left: -300px; }
|
||||
to { left: 0; }
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
}
|
||||
button.animated {
|
||||
animation: 1s linear 1s move forwards;
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; width: 300px; height: 300px;">
|
||||
<button onclick="window.clicked=true"></button>
|
||||
</div>
|
||||
`);
|
||||
const handle = await page.$('button');
|
||||
const promise = handle.click();
|
||||
await handle.evaluate(button => button.className = 'animated');
|
||||
await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(true);
|
||||
});
|
||||
it('should fail when element is animating from outside the viewport with force', async({page, server}) => {
|
||||
await page.setContent(`<style>
|
||||
@keyframes move {
|
||||
from { left: -300px; }
|
||||
to { left: 0; }
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
}
|
||||
button.animated {
|
||||
animation: 1s linear 1s move forwards;
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; width: 300px; height: 300px;">
|
||||
<button onclick="window.clicked=true"></button>
|
||||
</div>
|
||||
`);
|
||||
const handle = await page.$('button');
|
||||
const promise = handle.click({ force: true }).catch(e => e);
|
||||
await handle.evaluate(button => button.className = 'animated');
|
||||
const error = await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('Element is outside of the viewport');
|
||||
});
|
||||
it('should dispatch microtasks in order', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<button id=button>Click me</button>
|
||||
<script>
|
||||
let mutationCount = 0;
|
||||
const observer = new MutationObserver((mutationsList, observer) => {
|
||||
for(let mutation of mutationsList)
|
||||
++mutationCount;
|
||||
});
|
||||
observer.observe(document.body, { attributes: true, childList: true, subtree: true });
|
||||
button.addEventListener('mousedown', () => {
|
||||
mutationCount = 0;
|
||||
document.body.appendChild(document.createElement('div'));
|
||||
});
|
||||
button.addEventListener('mouseup', () => {
|
||||
window.result = mutationCount;
|
||||
});
|
||||
</script>
|
||||
`);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => window.result)).toBe(1);
|
||||
});
|
||||
it.fail(true)('should retarget when element is recycled during hit testing', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
const __testHookAfterStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));
|
||||
});
|
||||
await page.click('text=button1', { __testHookAfterStable });
|
||||
expect(await page.evaluate(() => window.button1)).toBe(true);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
});
|
||||
it.fail(true)('should retarget when element is recycled before enabled check', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] ));
|
||||
});
|
||||
const __testHookBeforeStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] ));
|
||||
});
|
||||
await page.click('text=button1', { __testHookBeforeStable });
|
||||
expect(await page.evaluate(() => window.button1)).toBe(true);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
});
|
||||
it('should not retarget when element changes on hover', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1', renameOnHover: true }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
await page.click('text=button1');
|
||||
expect(await page.evaluate(() => window.button1)).toBe(true);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
});
|
||||
it('should not retarget when element is recycled on hover', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
function shuffle() {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));
|
||||
}
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1', onHover: shuffle }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
await page.click('text=button1');
|
||||
expect(await page.evaluate(() => window.button1)).toBe(undefined);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(true);
|
||||
});
|
||||
it('should click the button when window.innerWidth is corrupted', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.evaluate(() => window.innerWidth = 0);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
});
|
||||
776
test/click.spec.js
Normal file
776
test/click.spec.js
Normal file
|
|
@ -0,0 +1,776 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, HEADLESS, USES_HOOKS} = testOptions;
|
||||
|
||||
async function giveItAChanceToClick(page) {
|
||||
for (let i = 0; i < 5; i++)
|
||||
await page.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
|
||||
}
|
||||
|
||||
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('should click svg', async({page, server}) => {
|
||||
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('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/GoogleChrome/puppeteer/issues/4281
|
||||
it('should click on a span with an inline element inside', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
span::before {
|
||||
content: 'q';
|
||||
}
|
||||
</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({browser, server}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await Promise.all([
|
||||
page.close(),
|
||||
page.mouse.click(1, 2),
|
||||
]).catch(e => {});
|
||||
await context.close();
|
||||
});
|
||||
|
||||
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('should click the button after a cross origin navigation ', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/input/button.html');
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should click with disabled javascript', async({browser, server}) => {
|
||||
const context = await browser.newContext({ javaScriptEnabled: false });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/wrappedlink.html');
|
||||
await Promise.all([
|
||||
page.click('a'),
|
||||
page.waitForNavigation()
|
||||
]);
|
||||
expect(page.url()).toBe(server.PREFIX + '/wrappedlink.html#clicked');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should click when one of inline box children is outside of viewport', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
i {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
}
|
||||
</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');
|
||||
const text = 'This is the text that we are going to try to select. Let\'s see how it goes.';
|
||||
await page.fill('textarea', text);
|
||||
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('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 waitFor visible when already visible', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should not wait with force', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
await page.click('button', { force: true }).catch(e => error = e);
|
||||
expect(error.message).toContain('Element is not visible');
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
});
|
||||
|
||||
it('should waitFor display:none to be gone', async({page, server}) => {
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.display = 'none');
|
||||
const clicked = page.click('button', { timeout: 0 }).then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('button', b => b.style.display = 'block');
|
||||
await clicked;
|
||||
expect(done).toBe(true);
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should waitFor visibility:hidden to be gone', async({page, server}) => {
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.style.visibility = 'hidden');
|
||||
const clicked = page.click('button', { timeout: 0 }).then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => result)).toBe('Was not clicked');
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('button', b => b.style.visibility = 'visible');
|
||||
await clicked;
|
||||
expect(done).toBe(true);
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should waitFor visible when parent is hidden', async({page, server}) => {
|
||||
let done = false;
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', b => b.parentElement.style.display = 'none');
|
||||
const clicked = page.click('button', { timeout: 0 }).then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(done).toBe(false);
|
||||
await page.$eval('button', b => b.parentElement.style.display = 'block');
|
||||
await clicked;
|
||||
expect(done).toBe(true);
|
||||
expect(await page.evaluate(() => result)).toBe('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('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 not hang with touch-enabled viewports', async({browser, playwright}) => {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/161
|
||||
const { viewport, hasTouch } = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ viewport, hasTouch });
|
||||
const page = await context.newPage();
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(100, 10);
|
||||
await page.mouse.up();
|
||||
await context.close();
|
||||
});
|
||||
|
||||
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('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;
|
||||
});
|
||||
});
|
||||
await page.dblclick('button');
|
||||
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');
|
||||
});
|
||||
|
||||
it('should click links which cause navigation', async({page, server}) => {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/206
|
||||
await page.setContent(`<a href="${server.EMPTY_PAGE}">empty.html</a>`);
|
||||
// This await should not hang.
|
||||
await page.click('a');
|
||||
});
|
||||
|
||||
it('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');
|
||||
});
|
||||
|
||||
it.fail(CHROMIUM || WEBKIT)('should click the button with fixed position inside an iframe', async({page, server}) => {
|
||||
// @see https://github.com/GoogleChrome/puppeteer/issues/4110
|
||||
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390
|
||||
// @see https://chromium-review.googlesource.com/c/chromium/src/+/1742784
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setViewportSize({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('should click the button with deviceScaleFactor set', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 400, height: 400 }, deviceScaleFactor: 5 });
|
||||
const page = await context.newPage();
|
||||
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 context.close();
|
||||
});
|
||||
|
||||
it('should click the button with px border with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '8px');
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 20 + 8 : 20);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 10 + 8 : 10);
|
||||
});
|
||||
|
||||
it('should click the button with em border with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '2em');
|
||||
await page.$eval('button', button => button.style.fontSize = '12px');
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 12 * 2 + 20 : 20);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 12 * 2 + 10 : 10);
|
||||
});
|
||||
|
||||
it('should click a very large button with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => button.style.borderWidth = '8px');
|
||||
await page.$eval('button', button => button.style.height = button.style.width = '2000px');
|
||||
await page.click('button', { position: { x: 1900, y: 1910 } });
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910);
|
||||
});
|
||||
|
||||
it('should click a button in scrolling container with offset', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
const container = document.createElement('div');
|
||||
container.style.overflow = 'auto';
|
||||
container.style.width = '200px';
|
||||
container.style.height = '200px';
|
||||
button.parentElement.insertBefore(container, button);
|
||||
container.appendChild(button);
|
||||
button.style.height = '2000px';
|
||||
button.style.width = '2000px';
|
||||
button.style.borderWidth = '8px';
|
||||
});
|
||||
await page.click('button', { position: { x: 1900, y: 1910 } });
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
// Safari reports border-relative offsetX/offsetY.
|
||||
expect(await page.evaluate(() => offsetX)).toBe(WEBKIT ? 1900 + 8 : 1900);
|
||||
expect(await page.evaluate(() => offsetY)).toBe(WEBKIT ? 1910 + 8 : 1910);
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should click the button with offset with page scale', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
button.style.borderWidth = '8px';
|
||||
document.body.style.margin = '0';
|
||||
});
|
||||
await page.click('button', { position: { x: 20, y: 10 } });
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
const round = x => Math.round(x + 0.01);
|
||||
let expected = { x: 28, y: 18 }; // 20;10 + 8px of border in each direction
|
||||
if (WEBKIT) {
|
||||
// WebKit rounds up during css -> dip -> css conversion.
|
||||
expected = { x: 29, y: 19 };
|
||||
} else if (CHROMIUM && HEADLESS) {
|
||||
// Headless Chromium rounds down during css -> dip -> css conversion.
|
||||
expected = { x: 27, y: 18 };
|
||||
}
|
||||
expect(round(await page.evaluate(() => pageX))).toBe(expected.x);
|
||||
expect(round(await page.evaluate(() => pageY))).toBe(expected.y);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should wait for stable position', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
button.style.transition = 'margin 500ms linear 0s';
|
||||
button.style.marginLeft = '200px';
|
||||
button.style.borderWidth = '0';
|
||||
button.style.width = '200px';
|
||||
button.style.height = '20px';
|
||||
// Set display to "block" - otherwise Firefox layouts with non-even
|
||||
// values on Linux.
|
||||
button.style.display = 'block';
|
||||
document.body.style.margin = '0';
|
||||
});
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
expect(await page.evaluate(() => pageX)).toBe(300);
|
||||
expect(await page.evaluate(() => pageY)).toBe(10);
|
||||
});
|
||||
|
||||
it('should wait for becoming hit target', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.$eval('button', button => {
|
||||
button.style.borderWidth = '0';
|
||||
button.style.width = '200px';
|
||||
button.style.height = '20px';
|
||||
document.body.style.margin = '0';
|
||||
document.body.style.position = 'relative';
|
||||
const flyOver = document.createElement('div');
|
||||
flyOver.className = 'flyover';
|
||||
flyOver.style.position = 'absolute';
|
||||
flyOver.style.width = '400px';
|
||||
flyOver.style.height = '20px';
|
||||
flyOver.style.left = '-200px';
|
||||
flyOver.style.top = '0';
|
||||
flyOver.style.background = 'red';
|
||||
document.body.appendChild(flyOver);
|
||||
});
|
||||
let clicked = false;
|
||||
const clickPromise = page.click('button').then(() => clicked = true);
|
||||
expect(clicked).toBe(false);
|
||||
|
||||
await page.$eval('.flyover', flyOver => flyOver.style.left = '0');
|
||||
await giveItAChanceToClick(page);
|
||||
expect(clicked).toBe(false);
|
||||
|
||||
await page.$eval('.flyover', flyOver => flyOver.style.left = '200px');
|
||||
await clickPromise;
|
||||
expect(clicked).toBe(true);
|
||||
expect(await page.evaluate(() => window.result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should fail when obscured and not waiting for hit target', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(() => {
|
||||
document.body.style.position = 'relative';
|
||||
const blocker = document.createElement('div');
|
||||
blocker.style.position = 'absolute';
|
||||
blocker.style.width = '400px';
|
||||
blocker.style.height = '20px';
|
||||
blocker.style.left = '0';
|
||||
blocker.style.top = '0';
|
||||
document.body.appendChild(blocker);
|
||||
});
|
||||
await button.click({ force: true });
|
||||
expect(await page.evaluate(() => window.result)).toBe('Was not clicked');
|
||||
});
|
||||
|
||||
it('should wait for button to be enabled', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;" disabled><span>Click target</span></button>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('text=Click target').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('button').removeAttribute('disabled'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should wait for input to be enabled', async({page, server}) => {
|
||||
await page.setContent('<input onclick="javascript:window.__CLICKED=true;" disabled>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('input').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('input').removeAttribute('disabled'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should wait for select to be enabled', async({page, server}) => {
|
||||
await page.setContent('<select onclick="javascript:window.__CLICKED=true;" disabled><option selected>Hello</option></select>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('select').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('select').removeAttribute('disabled'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should click disabled div', async({page, server}) => {
|
||||
await page.setContent('<div onclick="javascript:window.__CLICKED=true;" disabled>Click target</div>');
|
||||
await page.click('text=Click target');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should climb dom for inner label with pointer-events:none', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;"><label style="pointer-events:none">Click target</label></button>');
|
||||
await page.click('text=Click target');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should climb up to [role=button]', async({page, server}) => {
|
||||
await page.setContent('<div role=button onclick="javascript:window.__CLICKED=true;"><div style="pointer-events:none"><span><div>Click target</div></span></div>');
|
||||
await page.click('text=Click target');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should wait for BUTTON to be clickable when it has pointer-events:none', async({page, server}) => {
|
||||
await page.setContent('<button onclick="javascript:window.__CLICKED=true;" style="pointer-events:none"><span>Click target</span></button>');
|
||||
let done = false;
|
||||
const clickPromise = page.click('text=Click target').then(() => done = true);
|
||||
await giveItAChanceToClick(page);
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
expect(done).toBe(false);
|
||||
await page.evaluate(() => document.querySelector('button').style.removeProperty('pointer-events'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should wait for LABEL to be clickable when it has pointer-events:none', async({page, server}) => {
|
||||
await page.setContent('<label onclick="javascript:window.__CLICKED=true;" style="pointer-events:none"><span>Click target</span></label>');
|
||||
const clickPromise = page.click('text=Click target');
|
||||
// Do a few roundtrips to the page.
|
||||
for (let i = 0; i < 5; ++i)
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(undefined);
|
||||
// remove `pointer-events: none` css from button.
|
||||
await page.evaluate(() => document.querySelector('label').style.removeProperty('pointer-events'));
|
||||
await clickPromise;
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(true);
|
||||
});
|
||||
|
||||
it('should update modifiers correctly', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.click('button', { modifiers: ['Shift'] });
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(true);
|
||||
await page.click('button', { modifiers: [] });
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(false);
|
||||
|
||||
await page.keyboard.down('Shift');
|
||||
await page.click('button', { modifiers: [] });
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(false);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(true);
|
||||
await page.keyboard.up('Shift');
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => shiftKey)).toBe(false);
|
||||
});
|
||||
|
||||
it('should click an offscreen element when scroll-behavior is smooth', async({page}) => {
|
||||
await page.setContent(`
|
||||
<div style="border: 1px solid black; height: 500px; overflow: auto; width: 500px; scroll-behavior: smooth">
|
||||
<button style="margin-top: 2000px" onClick="window.clicked = true">hi</button>
|
||||
</div>
|
||||
`);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate('window.clicked')).toBe(true);
|
||||
});
|
||||
|
||||
it('should report nice error when element is detached and force-clicked', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/animating-button.html');
|
||||
await page.evaluate(() => addButton());
|
||||
const handle = await page.$('button');
|
||||
await page.evaluate(() => stopButton(true));
|
||||
const promise = handle.click({ force: true }).catch(e => e);
|
||||
const error = await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
|
||||
it('should fail when element detaches after animation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/animating-button.html');
|
||||
await page.evaluate(() => addButton());
|
||||
const handle = await page.$('button');
|
||||
const promise = handle.click().catch(e => e);
|
||||
await page.evaluate(() => stopButton(true));
|
||||
const error = await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('Element is not attached to the DOM');
|
||||
});
|
||||
|
||||
it('should retry when element detaches after animation', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/animating-button.html');
|
||||
await page.evaluate(() => addButton());
|
||||
let clicked = false;
|
||||
const promise = page.click('button').then(() => clicked = true);
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
await page.evaluate(() => stopButton(true));
|
||||
await page.evaluate(() => addButton());
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
await page.evaluate(() => stopButton(true));
|
||||
await page.evaluate(() => addButton());
|
||||
expect(clicked).toBe(false);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
await page.evaluate(() => stopButton(false));
|
||||
await promise;
|
||||
expect(clicked).toBe(true);
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should retry when element is animating from outside the viewport', async({page, server}) => {
|
||||
await page.setContent(`<style>
|
||||
@keyframes move {
|
||||
from { left: -300px; }
|
||||
to { left: 0; }
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
}
|
||||
button.animated {
|
||||
animation: 1s linear 1s move forwards;
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; width: 300px; height: 300px;">
|
||||
<button onclick="window.clicked=true"></button>
|
||||
</div>
|
||||
`);
|
||||
const handle = await page.$('button');
|
||||
const promise = handle.click();
|
||||
await handle.evaluate(button => button.className = 'animated');
|
||||
await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should fail when element is animating from outside the viewport with force', async({page, server}) => {
|
||||
await page.setContent(`<style>
|
||||
@keyframes move {
|
||||
from { left: -300px; }
|
||||
to { left: 0; }
|
||||
}
|
||||
button {
|
||||
position: absolute;
|
||||
left: -300px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 200px;
|
||||
}
|
||||
button.animated {
|
||||
animation: 1s linear 1s move forwards;
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; width: 300px; height: 300px;">
|
||||
<button onclick="window.clicked=true"></button>
|
||||
</div>
|
||||
`);
|
||||
const handle = await page.$('button');
|
||||
const promise = handle.click({ force: true }).catch(e => e);
|
||||
await handle.evaluate(button => button.className = 'animated');
|
||||
const error = await promise;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(undefined);
|
||||
expect(error.message).toContain('Element is outside of the viewport');
|
||||
});
|
||||
|
||||
it('should dispatch microtasks in order', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<button id=button>Click me</button>
|
||||
<script>
|
||||
let mutationCount = 0;
|
||||
const observer = new MutationObserver((mutationsList, observer) => {
|
||||
for(let mutation of mutationsList)
|
||||
++mutationCount;
|
||||
});
|
||||
observer.observe(document.body, { attributes: true, childList: true, subtree: true });
|
||||
button.addEventListener('mousedown', () => {
|
||||
mutationCount = 0;
|
||||
document.body.appendChild(document.createElement('div'));
|
||||
});
|
||||
button.addEventListener('mouseup', () => {
|
||||
window.result = mutationCount;
|
||||
});
|
||||
</script>
|
||||
`);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => window.result)).toBe(1);
|
||||
});
|
||||
|
||||
it.fail(true)('should retarget when element is recycled during hit testing', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
const __testHookAfterStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));
|
||||
});
|
||||
await page.click('text=button1', { __testHookAfterStable });
|
||||
expect(await page.evaluate(() => window.button1)).toBe(true);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
});
|
||||
|
||||
it.fail(true)('should retarget when element is recycled before enabled check', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })] ));
|
||||
});
|
||||
const __testHookBeforeStable = () => page.evaluate(() => {
|
||||
window.counter = (window.counter || 0) + 1;
|
||||
if (window.counter === 1)
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2', disabled: true }), e(MyButton, { name: 'button1' })] ));
|
||||
});
|
||||
await page.click('text=button1', { __testHookBeforeStable });
|
||||
expect(await page.evaluate(() => window.button1)).toBe(true);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should not retarget when element changes on hover', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1', renameOnHover: true }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
await page.click('text=button1');
|
||||
expect(await page.evaluate(() => window.button1)).toBe(true);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should not retarget when element is recycled on hover', async ({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/react.html');
|
||||
await page.evaluate(() => {
|
||||
function shuffle() {
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button2' }), e(MyButton, { name: 'button1' })] ));
|
||||
}
|
||||
renderComponent(e('div', {}, [e(MyButton, { name: 'button1', onHover: shuffle }), e(MyButton, { name: 'button2' })] ));
|
||||
});
|
||||
await page.click('text=button1');
|
||||
expect(await page.evaluate(() => window.button1)).toBe(undefined);
|
||||
expect(await page.evaluate(() => window.button2)).toBe(true);
|
||||
});
|
||||
|
||||
it('should click the button when window.innerWidth is corrupted', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.evaluate(() => window.innerWidth = 0);
|
||||
await page.click('button');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
|
@ -1,508 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, WIN} = testOptions;
|
||||
|
||||
describe('BrowserContext.cookies', function() {
|
||||
it('should return no cookies in pristine browser context', async({context, page, server}) => {
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
});
|
||||
it('should get a cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
return document.cookie;
|
||||
});
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should get a non-session cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
const documentCookie = await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
return document.cookie;
|
||||
}, date);
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: date / 1000,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should properly report httpOnly cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;HttpOnly; Path=/');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].httpOnly).toBe(true);
|
||||
});
|
||||
it.fail(WEBKIT && WIN)('should properly report "Strict" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Strict');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Strict');
|
||||
});
|
||||
it.fail(WEBKIT && WIN)('should properly report "Lax" sameSite cookie', async({context, page, server}) => {
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'name=value;SameSite=Lax');
|
||||
res.end();
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies[0].sameSite).toBe('Lax');
|
||||
});
|
||||
it('should get multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
document.cookie = 'password=1234';
|
||||
return document.cookie.split('; ').sort().join('; ');
|
||||
});
|
||||
const cookies = await context.cookies();
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(documentCookie).toBe('password=1234; username=John Doe');
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
name: 'password',
|
||||
value: '1234',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should get cookies from multiple urls', async({context}) => {
|
||||
await context.addCookies([{
|
||||
url: 'https://foo.com',
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
}, {
|
||||
url: 'https://bar.com',
|
||||
name: 'catto',
|
||||
value: 'purrs',
|
||||
}, {
|
||||
url: 'https://baz.com',
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
}]);
|
||||
const cookies = await context.cookies(['https://foo.com', 'https://baz.com']);
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
expect(cookies).toEqual([{
|
||||
name: 'birdo',
|
||||
value: 'tweets',
|
||||
domain: 'baz.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}, {
|
||||
name: 'doggo',
|
||||
value: 'woofs',
|
||||
domain: 'foo.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.addCookies', function() {
|
||||
it('should work', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'password',
|
||||
value: '123456'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toEqual('password=123456');
|
||||
});
|
||||
it('should roundtrip cookie', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
// @see https://en.wikipedia.org/wiki/Year_2038_problem
|
||||
const date = +(new Date('1/1/2038'));
|
||||
const documentCookie = await page.evaluate(timestamp => {
|
||||
const date = new Date(timestamp);
|
||||
document.cookie = `username=John Doe;expires=${date.toUTCString()}`;
|
||||
return document.cookie;
|
||||
}, date);
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
const cookies = await context.cookies();
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await context.addCookies(cookies);
|
||||
expect(await context.cookies()).toEqual(cookies);
|
||||
});
|
||||
it('should send cookie header', async({server, context}) => {
|
||||
let cookie = '';
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie;
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'cookie', value: 'value'}]);
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('cookie=value');
|
||||
});
|
||||
it('should isolate cookies in browser contexts', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'isolatecookie', value: 'page2value'}]);
|
||||
|
||||
const cookies1 = await context.cookies();
|
||||
const cookies2 = await anotherContext.cookies();
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies2.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('isolatecookie');
|
||||
expect(cookies1[0].value).toBe('page1value');
|
||||
expect(cookies2[0].name).toBe('isolatecookie');
|
||||
expect(cookies2[0].value).toBe('page2value');
|
||||
await anotherContext.close();
|
||||
});
|
||||
it('should isolate session cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'session=value');
|
||||
res.end();
|
||||
});
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
}
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.length).toBe(1);
|
||||
expect(cookies.map(c => c.value).join(',')).toBe('value');
|
||||
}
|
||||
{
|
||||
const context2 = await browser.newContext();
|
||||
const page = await context2.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies[0] && cookies[0].name).toBe(undefined);
|
||||
await context2.close();
|
||||
}
|
||||
});
|
||||
it('should isolate persistent cookies', async({context, server, browser}) => {
|
||||
server.setRoute('/setcookie.html', (req, res) => {
|
||||
res.setHeader('Set-Cookie', 'persistent=persistent-value; max-age=3600');
|
||||
res.end();
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/setcookie.html');
|
||||
|
||||
const context1 = context;
|
||||
const context2 = await browser.newContext();
|
||||
const [page1, page2] = await Promise.all([context1.newPage(), context2.newPage()]);
|
||||
await Promise.all([page1.goto(server.EMPTY_PAGE), page2.goto(server.EMPTY_PAGE)]);
|
||||
const [cookies1, cookies2] = await Promise.all([context1.cookies(), context2.cookies()]);
|
||||
expect(cookies1.length).toBe(1);
|
||||
expect(cookies1[0].name).toBe('persistent');
|
||||
expect(cookies1[0].value).toBe('persistent-value');
|
||||
expect(cookies2.length).toBe(0);
|
||||
await context2.close();
|
||||
});
|
||||
it('should isolate send cookie header', async({server, context, browser}) => {
|
||||
let cookie = [];
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
cookie = req.headers.cookie || '';
|
||||
res.end();
|
||||
});
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'sendcookie', value: 'value'}]);
|
||||
{
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('sendcookie=value');
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(cookie).toBe('');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it.slow()('should isolate cookies between launches', async({browserType, server, defaultBrowserOptions}) => {
|
||||
const browser1 = await browserType.launch(defaultBrowserOptions);
|
||||
const context1 = await browser1.newContext();
|
||||
await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]);
|
||||
await browser1.close();
|
||||
|
||||
const browser2 = await browserType.launch(defaultBrowserOptions);
|
||||
const context2 = await browser2.newContext();
|
||||
const cookies = await context2.cookies();
|
||||
expect(cookies.length).toBe(0);
|
||||
await browser2.close();
|
||||
});
|
||||
it('should set multiple cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-1',
|
||||
value: '123456'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'multiple-2',
|
||||
value: 'bar'
|
||||
}]);
|
||||
expect(await page.evaluate(() => {
|
||||
const cookies = document.cookie.split(';');
|
||||
return cookies.map(cookie => cookie.trim()).sort();
|
||||
})).toEqual([
|
||||
'multiple-1=123456',
|
||||
'multiple-2=bar',
|
||||
]);
|
||||
});
|
||||
it('should have |expires| set to |-1| for session cookies', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'expires',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies[0].expires).toBe(-1);
|
||||
});
|
||||
it('should set cookie with reasonable defaults', async({context, server}) => {
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'defaults',
|
||||
value: '123456'
|
||||
}]);
|
||||
const cookies = await context.cookies();
|
||||
expect(cookies.sort((a, b) => a.name.localeCompare(b.name))).toEqual([{
|
||||
name: 'defaults',
|
||||
value: '123456',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set a cookie with a path', async({context, page, server}) => {
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
await context.addCookies([{
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
}]);
|
||||
expect(await context.cookies()).toEqual([{
|
||||
name: 'gridcookie',
|
||||
value: 'GRID',
|
||||
domain: 'localhost',
|
||||
path: '/grid.html',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
|
||||
});
|
||||
it('should not set a cookie with blank page URL', async function({context, server}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([
|
||||
{url: server.EMPTY_PAGE, name: 'example-cookie', value: 'best'},
|
||||
{url: 'about:blank', name: 'example-cookie-blank', value: 'best'}
|
||||
]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain(
|
||||
`Blank page can not have cookie "example-cookie-blank"`
|
||||
);
|
||||
});
|
||||
it('should not set a cookie on a data URL page', async function({context}) {
|
||||
let error = null;
|
||||
try {
|
||||
await context.addCookies([{url: 'data:,Hello%2C%20World!', name: 'example-cookie', value: 'best'}]);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
expect(error.message).toContain('Data URL page can not have cookie "example-cookie"');
|
||||
});
|
||||
it('should default to setting secure cookie for HTTPS websites', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const SECURE_URL = 'https://example.com';
|
||||
await context.addCookies([{
|
||||
url: SECURE_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(SECURE_URL);
|
||||
expect(cookie.secure).toBe(true);
|
||||
});
|
||||
it('should be able to set unsecure cookie for HTTP website', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const HTTP_URL = 'http://example.com';
|
||||
await context.addCookies([{
|
||||
url: HTTP_URL,
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
}]);
|
||||
const [cookie] = await context.cookies(HTTP_URL);
|
||||
expect(cookie.secure).toBe(false);
|
||||
});
|
||||
it('should set a cookie on a different domain', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: 'https://www.example.com',
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
expect(await context.cookies('https://www.example.com')).toEqual([{
|
||||
name: 'example-cookie',
|
||||
value: 'best',
|
||||
domain: 'www.example.com',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: true,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('should set cookies for a frame', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([
|
||||
{url: server.PREFIX, name: 'frame-cookie', value: 'value'}
|
||||
]);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.PREFIX + '/grid.html');
|
||||
|
||||
expect(await page.frames()[1].evaluate('document.cookie')).toBe('frame-cookie=value');
|
||||
});
|
||||
it('should(not) block third party cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
await page.frames()[1].evaluate(`document.cookie = 'username=John Doe'`);
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = CHROMIUM || FFOX;
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
"domain": "127.0.0.1",
|
||||
"expires": -1,
|
||||
"httpOnly": false,
|
||||
"name": "username",
|
||||
"path": "/",
|
||||
"sameSite": "None",
|
||||
"secure": false,
|
||||
"value": "John Doe"
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
expect(cookies).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext.clearCookies', function() {
|
||||
it('should clear cookies', async({context, page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await context.addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1');
|
||||
await context.clearCookies();
|
||||
expect(await context.cookies()).toEqual([]);
|
||||
await page.reload();
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
it('should isolate cookies when clearing', async({context, server, browser}) => {
|
||||
const anotherContext = await browser.newContext();
|
||||
await context.addCookies([{url: server.EMPTY_PAGE, name: 'page1cookie', value: 'page1value'}]);
|
||||
await anotherContext.addCookies([{url: server.EMPTY_PAGE, name: 'page2cookie', value: 'page2value'}]);
|
||||
|
||||
expect((await context.cookies()).length).toBe(1);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await context.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(1);
|
||||
|
||||
await anotherContext.clearCookies();
|
||||
expect((await context.cookies()).length).toBe(0);
|
||||
expect((await anotherContext.cookies()).length).toBe(0);
|
||||
await anotherContext.close();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 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 {FFOX, CHROMIUM, WEBKIT} = testOptions;
|
||||
|
||||
describe.skip(CHROMIUM)('Page.coverage missing', function() {
|
||||
it('should work', async function({page, server}) {
|
||||
expect(page.coverage).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(!CHROMIUM)('JSCoverage', function() {
|
||||
it('should work', async function({browserType, page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
it('should report sourceURLs', async function({page, server}) {
|
||||
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}) {
|
||||
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}) {
|
||||
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 playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage({reportAnonymousScripts: true});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate('console.log("foo")');
|
||||
await page.evaluate(() => console.log('bar'));
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
it('should report multiple scripts', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
expect(coverage[0].url).toContain('/jscoverage/script1.js');
|
||||
expect(coverage[1].url).toContain('/jscoverage/script2.js');
|
||||
});
|
||||
describe('resetOnNavigation', function() {
|
||||
it('should report scripts across navigations when disabled', async function({page, server}) {
|
||||
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}) {
|
||||
await page.coverage.startJSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/jscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
});
|
||||
it('should not hang when there is a debugger statement', async function({page, server}) {
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => {
|
||||
debugger; // eslint-disable-line no-debugger
|
||||
});
|
||||
await page.coverage.stopJSCoverage();
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(!CHROMIUM)('CSSCoverage', function() {
|
||||
it('should work', async function({browserType, page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/simple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/simple.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 1, end: 22}
|
||||
]);
|
||||
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}) {
|
||||
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}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(2);
|
||||
coverage.sort((a, b) => a.url.localeCompare(b.url));
|
||||
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}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/unused.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toBe('unused.css');
|
||||
expect(coverage[0].ranges.length).toBe(0);
|
||||
});
|
||||
it('should work with media queries', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.goto(server.PREFIX + '/csscoverage/media.html');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/csscoverage/media.html');
|
||||
expect(coverage[0].ranges).toEqual([
|
||||
{start: 17, end: 38}
|
||||
]);
|
||||
});
|
||||
it('should work with complicated usecases', async function({page, server}) {
|
||||
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>/')).toMatchSnapshot();
|
||||
});
|
||||
it('should ignore injected stylesheets', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.addStyleTag({content: 'body { margin: 10px;}'});
|
||||
// trigger style recalc
|
||||
const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin);
|
||||
expect(margin).toBe('10px');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
describe('resetOnNavigation', function() {
|
||||
it('should report stylesheets across navigations', async function({page, server}) {
|
||||
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}) {
|
||||
await page.coverage.startCSSCoverage(); // Enabled by default.
|
||||
await page.goto(server.PREFIX + '/csscoverage/multiple.html');
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(0);
|
||||
});
|
||||
});
|
||||
it('should work with a recently loaded stylesheet', async function({page, server}) {
|
||||
await page.coverage.startCSSCoverage();
|
||||
await page.evaluate(async url => {
|
||||
document.body.textContent = 'hello, world';
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = url;
|
||||
document.head.appendChild(link);
|
||||
await new Promise(x => link.onload = x);
|
||||
await new Promise(f => requestAnimationFrame(f));
|
||||
}, server.PREFIX + '/csscoverage/stylesheet1.css');
|
||||
const coverage = await page.coverage.stopCSSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,376 +0,0 @@
|
|||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const os = require('os');
|
||||
|
||||
const {mkdtempAsync, makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, MAC, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
|
||||
|
||||
registerFixture('userDataDir', async ({}, test) => {
|
||||
const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_profile-'));
|
||||
try {
|
||||
await test(userDataDir);
|
||||
} finally {
|
||||
removeFolderAsync(userDataDir).catch(e => {});
|
||||
}
|
||||
});
|
||||
|
||||
registerFixture('launchPersistent', async ({userDataDir, defaultBrowserOptions, browserType}, test) => {
|
||||
let context;
|
||||
async function launchPersistent(options) {
|
||||
if (context)
|
||||
throw new Error('can only launch one persitent context');
|
||||
context = await browserType.launchPersistentContext(userDataDir, {...defaultBrowserOptions, ...options});
|
||||
const page = context.pages()[0];
|
||||
return {context, page};
|
||||
}
|
||||
try {
|
||||
await test(launchPersistent);
|
||||
} finally {
|
||||
if (context)
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
describe('launchPersistentContext()', function() {
|
||||
it('context.cookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
return document.cookie;
|
||||
});
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('context.addCookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'username',
|
||||
value: 'John Doe'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
it('context.clearCookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie2',
|
||||
value: '2'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
|
||||
await page.context().clearCookies();
|
||||
await page.reload();
|
||||
expect(await page.context().cookies([])).toEqual([]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
it('should(not) block third party cookies', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
const documentCookie = await page.frames()[1].evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
return document.cookie;
|
||||
});
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = CHROMIUM || FFOX;
|
||||
expect(documentCookie).toBe(allowsThirdParty ? 'username=John Doe' : '');
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
"domain": "127.0.0.1",
|
||||
"expires": -1,
|
||||
"httpOnly": false,
|
||||
"name": "username",
|
||||
"path": "/",
|
||||
"sameSite": "None",
|
||||
"secure": false,
|
||||
"value": "John Doe"
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
expect(cookies).toEqual([]);
|
||||
}
|
||||
});
|
||||
it('should support viewport option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({viewport: { width: 456, height: 789 }});
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
const page2 = await context.newPage();
|
||||
await utils.verifyViewport(page2, 456, 789);
|
||||
});
|
||||
it('should support deviceScaleFactor option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({deviceScaleFactor: 3});
|
||||
expect(await page.evaluate('window.devicePixelRatio')).toBe(3);
|
||||
});
|
||||
it('should support userAgent option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({userAgent: 'foobar'});
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toBe('foobar');
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
});
|
||||
it('should support bypassCSP option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({bypassCSP: true});
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
});
|
||||
it('should support javascriptEnabled option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({javaScriptEnabled: false});
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
});
|
||||
it('should support httpCredentials option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({httpCredentials: { username: 'user', password: 'pass' }});
|
||||
server.setAuth('/playground.html', 'user', 'pass');
|
||||
const response = await page.goto(server.PREFIX + '/playground.html');
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
it('should support offline option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({offline: true});
|
||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
it.skip(true)('should support acceptDownloads option', async ({server, launchPersistent}) => {
|
||||
// TODO: unskip once we support downloads in persistent context.
|
||||
const {page, context} = await launchPersistent({acceptDownloads: true});
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
});
|
||||
it('should support hasTouch option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({hasTouch: true});
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
});
|
||||
it.skip(FFOX)('should work in persistent context', async ({server, launchPersistent}) => {
|
||||
// Firefox does not support mobile.
|
||||
const {page, context} = await launchPersistent({viewport: {width: 320, height: 480}, isMobile: true});
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
});
|
||||
it('should support colorScheme option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({colorScheme: 'dark'});
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
it('should support timezoneId option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({timezoneId: 'America/Jamaica'});
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
});
|
||||
it('should support locale option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({locale: 'fr-CH'});
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
});
|
||||
it('should support geolocation and permissions options', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({geolocation: {longitude: 10, latitude: 10}, permissions: ['geolocation']});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({latitude: 10, longitude: 10});
|
||||
});
|
||||
it('should support ignoreHTTPSErrors option', async ({httpsServer, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({ignoreHTTPSErrors: true});
|
||||
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 support extraHTTPHeaders option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({extraHTTPHeaders: { foo: 'bar' }});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
it('should accept userDataDir', async ({launchPersistent, userDataDir}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
// Note: we need an open page to make sure its functional.
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
await context.close();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
it.slow()('should restore state from userDataDir', async({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => localStorage.hey = 'hello');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
it.fail(CHROMIUM && (WIN || MAC)).slow()('should restore cookies from userDataDir', async({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
return document.cookie;
|
||||
});
|
||||
expect(documentCookie).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => document.cookie)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
it('should have default URL when launching browser', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
const urls = context.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
});
|
||||
it.skip(FFOX)('should throw if page argument is passed', async ({browserType, defaultBrowserOptions, server, userDataDir}) => {
|
||||
const options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('can not specify page');
|
||||
});
|
||||
it.skip(USES_HOOKS)('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server, userDataDir, toImpl}) => {
|
||||
const args = toImpl(browserType)._defaultArgs(defaultBrowserOptions, 'persistent', userDataDir, 0).filter(a => a !== 'about:blank');
|
||||
const options = {
|
||||
...defaultBrowserOptions,
|
||||
args: [...args, server.EMPTY_PAGE],
|
||||
ignoreDefaultArgs: true,
|
||||
};
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
if (!browserContext.pages().length)
|
||||
await browserContext.waitForEvent('page');
|
||||
await browserContext.pages()[0].waitForLoadState();
|
||||
const gotUrls = browserContext.pages().map(page => page.url());
|
||||
expect(gotUrls).toEqual([server.EMPTY_PAGE]);
|
||||
await browserContext.close();
|
||||
});
|
||||
it.skip(USES_HOOKS)('should handle timeout', async({browserType, defaultBrowserOptions, userDataDir}) => {
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain(`browserType.launchPersistentContext: Timeout 5000ms exceeded.`);
|
||||
});
|
||||
it.skip(USES_HOOKS)('should handle exception', async({browserType, defaultBrowserOptions, userDataDir}) => {
|
||||
const e = new Error('Dummy');
|
||||
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('Dummy');
|
||||
});
|
||||
it('should fire close event for a persistent context', async({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
let closed = false;
|
||||
context.on('close', () => closed = true);
|
||||
await context.close();
|
||||
expect(closed).toBe(true);
|
||||
});
|
||||
it.skip(!CHROMIUM)('coverage should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
it.skip(CHROMIUM)('coverage should be missing', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
expect(page.coverage).toBe(null);
|
||||
});
|
||||
});
|
||||
404
test/defaultbrowsercontext.spec.js
Normal file
404
test/defaultbrowsercontext.spec.js
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/**
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const os = require('os');
|
||||
|
||||
const {mkdtempAsync, makeUserDataDir, removeUserDataDir} = utils;
|
||||
const {FFOX, MAC, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
|
||||
|
||||
registerFixture('userDataDir', async ({}, test) => {
|
||||
const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_profile-'));
|
||||
try {
|
||||
await test(userDataDir);
|
||||
} finally {
|
||||
removeFolderAsync(userDataDir).catch(e => {});
|
||||
}
|
||||
});
|
||||
|
||||
registerFixture('launchPersistent', async ({userDataDir, defaultBrowserOptions, browserType}, test) => {
|
||||
let context;
|
||||
async function launchPersistent(options) {
|
||||
if (context)
|
||||
throw new Error('can only launch one persitent context');
|
||||
context = await browserType.launchPersistentContext(userDataDir, {...defaultBrowserOptions, ...options});
|
||||
const page = context.pages()[0];
|
||||
return {context, page};
|
||||
}
|
||||
try {
|
||||
await test(launchPersistent);
|
||||
} finally {
|
||||
if (context)
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('context.cookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
return document.cookie;
|
||||
});
|
||||
expect(documentCookie).toBe('username=John Doe');
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('context.addCookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'username',
|
||||
value: 'John Doe'
|
||||
}]);
|
||||
expect(await page.evaluate(() => document.cookie)).toBe('username=John Doe');
|
||||
expect(await page.context().cookies()).toEqual([{
|
||||
name: 'username',
|
||||
value: 'John Doe',
|
||||
domain: 'localhost',
|
||||
path: '/',
|
||||
expires: -1,
|
||||
httpOnly: false,
|
||||
secure: false,
|
||||
sameSite: 'None',
|
||||
}]);
|
||||
});
|
||||
|
||||
it('context.clearCookies() should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.context().addCookies([{
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie1',
|
||||
value: '1'
|
||||
}, {
|
||||
url: server.EMPTY_PAGE,
|
||||
name: 'cookie2',
|
||||
value: '2'
|
||||
}]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('cookie1=1; cookie2=2');
|
||||
await page.context().clearCookies();
|
||||
await page.reload();
|
||||
expect(await page.context().cookies([])).toEqual([]);
|
||||
expect(await page.evaluate('document.cookie')).toBe('');
|
||||
});
|
||||
|
||||
it('should(not) block third party cookies', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(src => {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
const iframe = document.createElement('iframe');
|
||||
document.body.appendChild(iframe);
|
||||
iframe.onload = fulfill;
|
||||
iframe.src = src;
|
||||
return promise;
|
||||
}, server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
const documentCookie = await page.frames()[1].evaluate(() => {
|
||||
document.cookie = 'username=John Doe';
|
||||
return document.cookie;
|
||||
});
|
||||
await page.waitForTimeout(2000);
|
||||
const allowsThirdParty = CHROMIUM || FFOX;
|
||||
expect(documentCookie).toBe(allowsThirdParty ? 'username=John Doe' : '');
|
||||
const cookies = await context.cookies(server.CROSS_PROCESS_PREFIX + '/grid.html');
|
||||
if (allowsThirdParty) {
|
||||
expect(cookies).toEqual([
|
||||
{
|
||||
"domain": "127.0.0.1",
|
||||
"expires": -1,
|
||||
"httpOnly": false,
|
||||
"name": "username",
|
||||
"path": "/",
|
||||
"sameSite": "None",
|
||||
"secure": false,
|
||||
"value": "John Doe"
|
||||
}
|
||||
]);
|
||||
} else {
|
||||
expect(cookies).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should support viewport option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({viewport: { width: 456, height: 789 }});
|
||||
await utils.verifyViewport(page, 456, 789);
|
||||
const page2 = await context.newPage();
|
||||
await utils.verifyViewport(page2, 456, 789);
|
||||
});
|
||||
|
||||
it('should support deviceScaleFactor option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({deviceScaleFactor: 3});
|
||||
expect(await page.evaluate('window.devicePixelRatio')).toBe(3);
|
||||
});
|
||||
|
||||
it('should support userAgent option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({userAgent: 'foobar'});
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toBe('foobar');
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['user-agent']).toBe('foobar');
|
||||
});
|
||||
|
||||
it('should support bypassCSP option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({bypassCSP: true});
|
||||
await page.goto(server.PREFIX + '/csp.html');
|
||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||
});
|
||||
|
||||
it('should support javascriptEnabled option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({javaScriptEnabled: false});
|
||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||
let error = null;
|
||||
await page.evaluate('something').catch(e => error = e);
|
||||
if (WEBKIT)
|
||||
expect(error.message).toContain('Can\'t find variable: something');
|
||||
else
|
||||
expect(error.message).toContain('something is not defined');
|
||||
});
|
||||
|
||||
it('should support httpCredentials option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({httpCredentials: { username: 'user', password: 'pass' }});
|
||||
server.setAuth('/playground.html', 'user', 'pass');
|
||||
const response = await page.goto(server.PREFIX + '/playground.html');
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should support offline option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({offline: true});
|
||||
const error = await page.goto(server.EMPTY_PAGE).catch(e => e);
|
||||
expect(error).toBeTruthy();
|
||||
});
|
||||
|
||||
it.skip(true)('should support acceptDownloads option', async ({server, launchPersistent}) => {
|
||||
// TODO: unskip once we support downloads in persistent context.
|
||||
const {page, context} = await launchPersistent({acceptDownloads: true});
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
});
|
||||
|
||||
it('should support hasTouch option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({hasTouch: true});
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should work in persistent context', async ({server, launchPersistent}) => {
|
||||
// Firefox does not support mobile.
|
||||
const {page, context} = await launchPersistent({viewport: {width: 320, height: 480}, isMobile: true});
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
});
|
||||
|
||||
it('should support colorScheme option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({colorScheme: 'dark'});
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
|
||||
it('should support timezoneId option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({timezoneId: 'America/Jamaica'});
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
});
|
||||
|
||||
it('should support locale option', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({locale: 'fr-CH'});
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
});
|
||||
|
||||
it('should support geolocation and permissions options', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({geolocation: {longitude: 10, latitude: 10}, permissions: ['geolocation']});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||
})));
|
||||
expect(geolocation).toEqual({latitude: 10, longitude: 10});
|
||||
});
|
||||
|
||||
it('should support ignoreHTTPSErrors option', async ({httpsServer, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({ignoreHTTPSErrors: true});
|
||||
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 support extraHTTPHeaders option', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent({extraHTTPHeaders: { foo: 'bar' }});
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['foo']).toBe('bar');
|
||||
});
|
||||
|
||||
it('should accept userDataDir', async ({launchPersistent, userDataDir}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
// Note: we need an open page to make sure its functional.
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
await context.close();
|
||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
});
|
||||
|
||||
it.slow()('should restore state from userDataDir', async({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.evaluate(() => localStorage.hey = 'hello');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
|
||||
it.fail(CHROMIUM && (WIN || MAC)).slow()('should restore cookies from userDataDir', async({browserType, defaultBrowserOptions, server, launchPersistent}) => {
|
||||
const userDataDir = await makeUserDataDir();
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page = await browserContext.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const documentCookie = await page.evaluate(() => {
|
||||
document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
return document.cookie;
|
||||
});
|
||||
expect(documentCookie).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext.close();
|
||||
|
||||
const browserContext2 = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
|
||||
const page2 = await browserContext2.newPage();
|
||||
await page2.goto(server.EMPTY_PAGE);
|
||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext2.close();
|
||||
|
||||
const userDataDir2 = await makeUserDataDir();
|
||||
const browserContext3 = await browserType.launchPersistentContext(userDataDir2, defaultBrowserOptions);
|
||||
const page3 = await browserContext3.newPage();
|
||||
await page3.goto(server.EMPTY_PAGE);
|
||||
expect(await page3.evaluate(() => document.cookie)).not.toBe('doSomethingOnlyOnce=true');
|
||||
await browserContext3.close();
|
||||
|
||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||
await removeUserDataDir(userDataDir);
|
||||
await removeUserDataDir(userDataDir2);
|
||||
});
|
||||
|
||||
it('should have default URL when launching browser', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
const urls = context.pages().map(page => page.url());
|
||||
expect(urls).toEqual(['about:blank']);
|
||||
});
|
||||
|
||||
it.skip(FFOX)('should throw if page argument is passed', async ({browserType, defaultBrowserOptions, server, userDataDir}) => {
|
||||
const options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('can not specify page');
|
||||
});
|
||||
|
||||
it.skip(USES_HOOKS)('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server, userDataDir, toImpl}) => {
|
||||
const args = toImpl(browserType)._defaultArgs(defaultBrowserOptions, 'persistent', userDataDir, 0).filter(a => a !== 'about:blank');
|
||||
const options = {
|
||||
...defaultBrowserOptions,
|
||||
args: [...args, server.EMPTY_PAGE],
|
||||
ignoreDefaultArgs: true,
|
||||
};
|
||||
const browserContext = await browserType.launchPersistentContext(userDataDir, options);
|
||||
if (!browserContext.pages().length)
|
||||
await browserContext.waitForEvent('page');
|
||||
await browserContext.pages()[0].waitForLoadState();
|
||||
const gotUrls = browserContext.pages().map(page => page.url());
|
||||
expect(gotUrls).toEqual([server.EMPTY_PAGE]);
|
||||
await browserContext.close();
|
||||
});
|
||||
|
||||
it.skip(USES_HOOKS)('should handle timeout', async({browserType, defaultBrowserOptions, userDataDir}) => {
|
||||
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain(`browserType.launchPersistentContext: Timeout 5000ms exceeded.`);
|
||||
});
|
||||
|
||||
it.skip(USES_HOOKS)('should handle exception', async({browserType, defaultBrowserOptions, userDataDir}) => {
|
||||
const e = new Error('Dummy');
|
||||
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } };
|
||||
const error = await browserType.launchPersistentContext(userDataDir, options).catch(e => e);
|
||||
expect(error.message).toContain('Dummy');
|
||||
});
|
||||
|
||||
it('should fire close event for a persistent context', async({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
let closed = false;
|
||||
context.on('close', () => closed = true);
|
||||
await context.close();
|
||||
expect(closed).toBe(true);
|
||||
});
|
||||
|
||||
it.skip(!CHROMIUM)('coverage should work', async ({server, launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
await page.coverage.startJSCoverage();
|
||||
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
|
||||
const coverage = await page.coverage.stopJSCoverage();
|
||||
expect(coverage.length).toBe(1);
|
||||
expect(coverage[0].url).toContain('/jscoverage/simple.html');
|
||||
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1);
|
||||
});
|
||||
|
||||
it.skip(CHROMIUM)('coverage should be missing', async ({launchPersistent}) => {
|
||||
const {page, context} = await launchPersistent();
|
||||
expect(page.coverage).toBe(null);
|
||||
});
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
|
||||
|
||||
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('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);
|
||||
});
|
||||
it('should accept the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.accept();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
it('should dismiss the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.dismiss();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
it.fail(CHANNEL)('should log prompt actions', async({browser}) => {
|
||||
const messages = [];
|
||||
const context = await browser.newContext({
|
||||
logger: {
|
||||
isEnabled: () => true,
|
||||
log: (name, severity, message) => messages.push(message),
|
||||
}
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const promise = page.evaluate(() => confirm('01234567890123456789012345678901234567890123456789012345678901234567890123456789'));
|
||||
const dialog = await page.waitForEvent('dialog');
|
||||
expect(messages.join()).toContain('confirm "0123456789012345678901234567890123456789012345678…" was shown');
|
||||
await dialog.accept('123');
|
||||
await promise;
|
||||
expect(messages.join()).toContain('confirm "0123456789012345678901234567890123456789012345678…" was accepted');
|
||||
await context.close();
|
||||
});
|
||||
it.fail(WEBKIT)('should be able to close context with open alert', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const alertPromise = page.waitForEvent('dialog');
|
||||
await page.evaluate(() => {
|
||||
setTimeout(() => alert('hello'), 0);
|
||||
});
|
||||
await alertPromise;
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
92
test/dialog.spec.js
Normal file
92
test/dialog.spec.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
|
||||
|
||||
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('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);
|
||||
});
|
||||
|
||||
it('should accept the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.accept();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should dismiss the confirm prompt', async({page, server}) => {
|
||||
page.on('dialog', dialog => {
|
||||
dialog.dismiss();
|
||||
});
|
||||
const result = await page.evaluate(() => confirm('boolean?'));
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it.fail(CHANNEL)('should log prompt actions', async({browser}) => {
|
||||
const messages = [];
|
||||
const context = await browser.newContext({
|
||||
logger: {
|
||||
isEnabled: () => true,
|
||||
log: (name, severity, message) => messages.push(message),
|
||||
}
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const promise = page.evaluate(() => confirm('01234567890123456789012345678901234567890123456789012345678901234567890123456789'));
|
||||
const dialog = await page.waitForEvent('dialog');
|
||||
expect(messages.join()).toContain('confirm "0123456789012345678901234567890123456789012345678…" was shown');
|
||||
await dialog.accept('123');
|
||||
await promise;
|
||||
expect(messages.join()).toContain('confirm "0123456789012345678901234567890123456789012345678…" was accepted');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it.fail(WEBKIT)('should be able to close context with open alert', async({browser}) => {
|
||||
const context = await browser.newContext();
|
||||
const page = await context.newPage();
|
||||
const alertPromise = page.waitForEvent('dialog');
|
||||
await page.evaluate(() => {
|
||||
setTimeout(() => alert('hello'), 0);
|
||||
});
|
||||
await alertPromise;
|
||||
await context.close();
|
||||
});
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
|
||||
|
||||
describe('Page.dispatchEvent(click)', function() {
|
||||
it('should dispatch click event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should dispatch click event properties', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => bubbles)).toBeTruthy();
|
||||
expect(await page.evaluate(() => cancelable)).toBeTruthy();
|
||||
expect(await page.evaluate(() => composed)).toBeTruthy();
|
||||
});
|
||||
it('should dispatch click svg', async({page, server}) => {
|
||||
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.dispatchEvent('circle', 'click');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
|
||||
});
|
||||
it('should dispatch click on a span with an inline element inside', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
span::before {
|
||||
content: 'q';
|
||||
}
|
||||
</style>
|
||||
<span onclick='javascript:window.CLICKED=42'></span>
|
||||
`);
|
||||
await page.dispatchEvent('span', 'click');
|
||||
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
|
||||
});
|
||||
it('should dispatch click after navigation ', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should dispatch click after a cross origin navigation ', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
it('should not fail when element is blocked on hover', async({page, server}) => {
|
||||
await page.setContent(`<style>
|
||||
container { display: block; position: relative; width: 200px; height: 50px; }
|
||||
div, button { position: absolute; left: 0; top: 0; bottom: 0; right: 0; }
|
||||
div { pointer-events: none; }
|
||||
container:hover div { pointer-events: auto; background: red; }
|
||||
</style>
|
||||
<container>
|
||||
<button onclick="window.clicked=true">Click me</button>
|
||||
<div></div>
|
||||
</container>`);
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => window.clicked)).toBeTruthy();
|
||||
});
|
||||
it('should dispatch click when node is added in shadow dom', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const watchdog = page.dispatchEvent('span', 'click');
|
||||
await page.evaluate(() => {
|
||||
const div = document.createElement('div');
|
||||
div.attachShadow({mode: 'open'});
|
||||
document.body.appendChild(div);
|
||||
});
|
||||
await page.evaluate(() => new Promise(f => setTimeout(f, 100)));
|
||||
await page.evaluate(() => {
|
||||
const span = document.createElement('span');
|
||||
span.textContent = 'Hello from shadow';
|
||||
span.addEventListener('click', () => window.clicked = true);
|
||||
document.querySelector('div').shadowRoot.appendChild(span);
|
||||
});
|
||||
await watchdog;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(true);
|
||||
});
|
||||
it('should be atomic', async({playwright, page}) => {
|
||||
const createDummySelector = () => ({
|
||||
create(root, target) {},
|
||||
query(root, selector) {
|
||||
const result = root.querySelector(selector);
|
||||
if (result)
|
||||
Promise.resolve().then(() => result.onclick = "");
|
||||
return result;
|
||||
},
|
||||
queryAll(root, selector) {
|
||||
const result = Array.from(root.querySelectorAll(selector));
|
||||
for (const e of result)
|
||||
Promise.resolve().then(() => result.onclick = "");
|
||||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'dispatchEvent', createDummySelector);
|
||||
await page.setContent(`<div onclick="window._clicked=true">Hello</div>`);
|
||||
await page.dispatchEvent('dispatchEvent=div', 'click');
|
||||
expect(await page.evaluate(() => window._clicked)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.dispatchEvent(drag)', function() {
|
||||
it.fail(WEBKIT)('should dispatch drag drop events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
await page.dispatchEvent('#source', 'dragstart', { dataTransfer });
|
||||
await page.dispatchEvent('#target', 'drop', { dataTransfer });
|
||||
expect(await page.evaluate(() => {
|
||||
return source.parentElement === target;
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.dispatchEvent(drag)', function() {
|
||||
it.fail(WEBKIT)('should dispatch drag drop events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
const source = await page.$('#source');
|
||||
await source.dispatchEvent('dragstart', { dataTransfer });
|
||||
const target = await page.$('#target');
|
||||
await target.dispatchEvent('drop', { dataTransfer });
|
||||
expect(await page.evaluate(() => {
|
||||
return source.parentElement === target;
|
||||
})).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ElementHandle.dispatchEvent(click)', function() {
|
||||
it('should dispatch click event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.dispatchEvent('click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
});
|
||||
156
test/dispatchevent.spec.js
Normal file
156
test/dispatchevent.spec.js
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/**
|
||||
* Copyright Microsoft Corporation. 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 utils = require('./utils');
|
||||
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
|
||||
|
||||
it('should dispatch click event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should dispatch click event properties', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => bubbles)).toBeTruthy();
|
||||
expect(await page.evaluate(() => cancelable)).toBeTruthy();
|
||||
expect(await page.evaluate(() => composed)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should dispatch click svg', async({page, server}) => {
|
||||
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.dispatchEvent('circle', 'click');
|
||||
expect(await page.evaluate(() => window.__CLICKED)).toBe(42);
|
||||
});
|
||||
|
||||
it('should dispatch click on a span with an inline element inside', async({page, server}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
span::before {
|
||||
content: 'q';
|
||||
}
|
||||
</style>
|
||||
<span onclick='javascript:window.CLICKED=42'></span>
|
||||
`);
|
||||
await page.dispatchEvent('span', 'click');
|
||||
expect(await page.evaluate(() => window.CLICKED)).toBe(42);
|
||||
});
|
||||
|
||||
it('should dispatch click after navigation ', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should dispatch click after a cross origin navigation ', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/input/button.html');
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
||||
it('should not fail when element is blocked on hover', async({page, server}) => {
|
||||
await page.setContent(`<style>
|
||||
container { display: block; position: relative; width: 200px; height: 50px; }
|
||||
div, button { position: absolute; left: 0; top: 0; bottom: 0; right: 0; }
|
||||
div { pointer-events: none; }
|
||||
container:hover div { pointer-events: auto; background: red; }
|
||||
</style>
|
||||
<container>
|
||||
<button onclick="window.clicked=true">Click me</button>
|
||||
<div></div>
|
||||
</container>`);
|
||||
await page.dispatchEvent('button', 'click');
|
||||
expect(await page.evaluate(() => window.clicked)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should dispatch click when node is added in shadow dom', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const watchdog = page.dispatchEvent('span', 'click');
|
||||
await page.evaluate(() => {
|
||||
const div = document.createElement('div');
|
||||
div.attachShadow({mode: 'open'});
|
||||
document.body.appendChild(div);
|
||||
});
|
||||
await page.evaluate(() => new Promise(f => setTimeout(f, 100)));
|
||||
await page.evaluate(() => {
|
||||
const span = document.createElement('span');
|
||||
span.textContent = 'Hello from shadow';
|
||||
span.addEventListener('click', () => window.clicked = true);
|
||||
document.querySelector('div').shadowRoot.appendChild(span);
|
||||
});
|
||||
await watchdog;
|
||||
expect(await page.evaluate(() => window.clicked)).toBe(true);
|
||||
});
|
||||
|
||||
it('should be atomic', async({playwright, page}) => {
|
||||
const createDummySelector = () => ({
|
||||
create(root, target) {},
|
||||
query(root, selector) {
|
||||
const result = root.querySelector(selector);
|
||||
if (result)
|
||||
Promise.resolve().then(() => result.onclick = "");
|
||||
return result;
|
||||
},
|
||||
queryAll(root, selector) {
|
||||
const result = Array.from(root.querySelectorAll(selector));
|
||||
for (const e of result)
|
||||
Promise.resolve().then(() => result.onclick = "");
|
||||
return result;
|
||||
}
|
||||
});
|
||||
await utils.registerEngine(playwright, 'dispatchEvent', createDummySelector);
|
||||
await page.setContent(`<div onclick="window._clicked=true">Hello</div>`);
|
||||
await page.dispatchEvent('dispatchEvent=div', 'click');
|
||||
expect(await page.evaluate(() => window._clicked)).toBe(true);
|
||||
});
|
||||
|
||||
it.fail(WEBKIT)('should dispatch drag drop events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
await page.dispatchEvent('#source', 'dragstart', { dataTransfer });
|
||||
await page.dispatchEvent('#target', 'drop', { dataTransfer });
|
||||
expect(await page.evaluate(() => {
|
||||
return source.parentElement === target;
|
||||
})).toBeTruthy();
|
||||
});
|
||||
|
||||
it.fail(WEBKIT)('should dispatch drag drop events', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/drag-n-drop.html');
|
||||
const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
|
||||
const source = await page.$('#source');
|
||||
await source.dispatchEvent('dragstart', { dataTransfer });
|
||||
const target = await page.$('#target');
|
||||
await target.dispatchEvent('drop', { dataTransfer });
|
||||
expect(await page.evaluate(() => {
|
||||
return source.parentElement === target;
|
||||
})).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should dispatch click event', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await button.dispatchEvent('click');
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
});
|
||||
|
|
@ -1,322 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 util = require('util');
|
||||
const os = require('os');
|
||||
const {mkdtempAsync, removeFolderAsync} = require('./utils');
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT, HEADLESS} = testOptions;
|
||||
|
||||
registerFixture('persistentDirectory', async ({}, test) => {
|
||||
const persistentDirectory = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
try {
|
||||
await test(persistentDirectory);
|
||||
} finally {
|
||||
await removeFolderAsync(persistentDirectory);
|
||||
}
|
||||
});
|
||||
|
||||
describe('Download', function() {
|
||||
beforeEach(async ({server}) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
server.setRoute('/downloadWithFilename', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX}/downloadWithFilename">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
let error;
|
||||
expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
await download.path().catch(e => error = e);
|
||||
expect(await download.failure()).toContain('acceptDownloads');
|
||||
expect(error.message).toContain('acceptDownloads: true');
|
||||
});
|
||||
it('should report downloads with acceptDownloads: true', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should save to user-specified path', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should save to user-specified path without updating original path', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
|
||||
const originalPath = await download.path();
|
||||
expect(fs.existsSync(originalPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(originalPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should save to two different paths with multiple saveAs calls', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
|
||||
const anotherUserPath = path.join(persistentDirectory, "download (2).txt");
|
||||
await download.saveAs(anotherUserPath);
|
||||
expect(fs.existsSync(anotherUserPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(anotherUserPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should save to overwritten filepath', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(persistentDirectory)).length).toBe(1);
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(persistentDirectory)).length).toBe(1);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should create subdirectories when saving to non-existent user-specified path', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const nestedPath = path.join(persistentDirectory, "these", "are", "directories", "download.txt");
|
||||
await download.saveAs(nestedPath)
|
||||
expect(fs.existsSync(nestedPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should error when saving with downloads disabled', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: false });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Pass { acceptDownloads: true } when you are creating your browser context');
|
||||
await page.close();
|
||||
});
|
||||
it('should error when saving after deletion', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.delete();
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Download already deleted. Save before deleting.');
|
||||
await page.close();
|
||||
});
|
||||
it('should report non-navigation downloads', async({browser, server}) => {
|
||||
// Mac WebKit embedder does not download in this case, although Safari does.
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a download="file.txt" href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it(`should report download path within page.on('download', …) handler for Files`, async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
const onDownloadPath = new Promise((res) => {
|
||||
page.on('download', dl => {
|
||||
dl.path().then(res);
|
||||
});
|
||||
});
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
await page.click('a');
|
||||
const path = await onDownloadPath;
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
})
|
||||
it(`should report download path within page.on('download', …) handler for Blobs`, async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
const onDownloadPath = new Promise((res) => {
|
||||
page.on('download', dl => {
|
||||
dl.path().then(res);
|
||||
});
|
||||
});
|
||||
await page.goto(server.PREFIX + '/download-blob.html');
|
||||
await page.click('a');
|
||||
const path = await onDownloadPath;
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
})
|
||||
it.fail(FFOX || WEBKIT)('should report alt-click downloads', async({browser, server}) => {
|
||||
// Firefox does not download on alt-click by default.
|
||||
// Our WebKit embedder does not download on alt-click, although Safari does.
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a', { modifiers: ['Alt']})
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it.fail(CHROMIUM && !HEADLESS)('should report new window downloads', async({browser, server}) => {
|
||||
// TODO: - the test fails in headful Chromium as the popup page gets closed along
|
||||
// with the session before download completed event arrives.
|
||||
// - WebKit doesn't close the popup page
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a target=_blank href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await page.close();
|
||||
});
|
||||
it('should delete file', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await download.delete();
|
||||
expect(fs.existsSync(path)).toBeFalsy();
|
||||
await page.close();
|
||||
});
|
||||
it('should expose stream', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const stream = await download.createReadStream();
|
||||
let content = '';
|
||||
stream.on('data', data => content += data.toString());
|
||||
await new Promise(f => stream.on('end', f));
|
||||
expect(content).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
it('should delete downloads on context destruction', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await page.context().close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
});
|
||||
it('should delete downloads on browser gone', async ({ server, browserType, defaultBrowserOptions }) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await browser.close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy();
|
||||
});
|
||||
});
|
||||
335
test/download.spec.js
Normal file
335
test/download.spec.js
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 util = require('util');
|
||||
const os = require('os');
|
||||
const {mkdtempAsync, removeFolderAsync} = require('./utils');
|
||||
|
||||
const {FFOX, CHROMIUM, WEBKIT, HEADLESS} = testOptions;
|
||||
|
||||
registerFixture('persistentDirectory', async ({}, test) => {
|
||||
const persistentDirectory = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
try {
|
||||
await test(persistentDirectory);
|
||||
} finally {
|
||||
await removeFolderAsync(persistentDirectory);
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async ({server}) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
server.setRoute('/downloadWithFilename', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: false', async({page, server}) => {
|
||||
await page.setContent(`<a href="${server.PREFIX}/downloadWithFilename">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
let error;
|
||||
expect(download.url()).toBe(`${server.PREFIX}/downloadWithFilename`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
await download.path().catch(e => error = e);
|
||||
expect(await download.failure()).toContain('acceptDownloads');
|
||||
expect(error.message).toContain('acceptDownloads: true');
|
||||
});
|
||||
|
||||
it('should report downloads with acceptDownloads: true', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to user-specified path', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to user-specified path without updating original path', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
|
||||
const originalPath = await download.path();
|
||||
expect(fs.existsSync(originalPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(originalPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to two different paths with multiple saveAs calls', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
|
||||
const anotherUserPath = path.join(persistentDirectory, "download (2).txt");
|
||||
await download.saveAs(anotherUserPath);
|
||||
expect(fs.existsSync(anotherUserPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(anotherUserPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should save to overwritten filepath', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(persistentDirectory)).length).toBe(1);
|
||||
await download.saveAs(userPath);
|
||||
expect((await util.promisify(fs.readdir)(persistentDirectory)).length).toBe(1);
|
||||
expect(fs.existsSync(userPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(userPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should create subdirectories when saving to non-existent user-specified path', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const nestedPath = path.join(persistentDirectory, "these", "are", "directories", "download.txt");
|
||||
await download.saveAs(nestedPath)
|
||||
expect(fs.existsSync(nestedPath)).toBeTruthy();
|
||||
expect(fs.readFileSync(nestedPath).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should error when saving with downloads disabled', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: false });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Pass { acceptDownloads: true } when you are creating your browser context');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should error when saving after deletion', async({persistentDirectory, browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const userPath = path.join(persistentDirectory, "download.txt");
|
||||
await download.delete();
|
||||
const { message } = await download.saveAs(userPath).catch(e => e);
|
||||
expect(message).toContain('Download already deleted. Save before deleting.');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should report non-navigation downloads', async({browser, server}) => {
|
||||
// Mac WebKit embedder does not download in this case, although Safari does.
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a download="file.txt" href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it(`should report download path within page.on('download', …) handler for Files`, async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
const onDownloadPath = new Promise((res) => {
|
||||
page.on('download', dl => {
|
||||
dl.path().then(res);
|
||||
});
|
||||
});
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
await page.click('a');
|
||||
const path = await onDownloadPath;
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
})
|
||||
it(`should report download path within page.on('download', …) handler for Blobs`, async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
const onDownloadPath = new Promise((res) => {
|
||||
page.on('download', dl => {
|
||||
dl.path().then(res);
|
||||
});
|
||||
});
|
||||
await page.goto(server.PREFIX + '/download-blob.html');
|
||||
await page.click('a');
|
||||
const path = await onDownloadPath;
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
})
|
||||
it.fail(FFOX || WEBKIT)('should report alt-click downloads', async({browser, server}) => {
|
||||
// Firefox does not download on alt-click by default.
|
||||
// Our WebKit embedder does not download on alt-click, although Safari does.
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a', { modifiers: ['Alt']})
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
expect(fs.readFileSync(path).toString()).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it.fail(CHROMIUM && !HEADLESS)('should report new window downloads', async({browser, server}) => {
|
||||
// TODO: - the test fails in headful Chromium as the popup page gets closed along
|
||||
// with the session before download completed event arrives.
|
||||
// - WebKit doesn't close the popup page
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a target=_blank href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should delete file', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await download.delete();
|
||||
expect(fs.existsSync(path)).toBeFalsy();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should expose stream', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const stream = await download.createReadStream();
|
||||
let content = '';
|
||||
stream.on('data', data => content += data.toString());
|
||||
await new Promise(f => stream.on('end', f));
|
||||
expect(content).toBe('Hello world');
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should delete downloads on context destruction', async({browser, server}) => {
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await page.context().close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should delete downloads on browser gone', async ({ server, browserType, defaultBrowserOptions }) => {
|
||||
const browser = await browserType.launch(defaultBrowserOptions);
|
||||
const page = await browser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download1 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const [ download2 ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path1 = await download1.path();
|
||||
const path2 = await download2.path();
|
||||
expect(fs.existsSync(path1)).toBeTruthy();
|
||||
expect(fs.existsSync(path2)).toBeTruthy();
|
||||
await browser.close();
|
||||
expect(fs.existsSync(path1)).toBeFalsy();
|
||||
expect(fs.existsSync(path2)).toBeFalsy();
|
||||
expect(fs.existsSync(path.join(path1, '..'))).toBeFalsy();
|
||||
});
|
||||
135
test/downloads-path.spec.js
Normal file
135
test/downloads-path.spec.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 fs = require('fs');
|
||||
const os = require('os');
|
||||
const {mkdtempAsync, removeFolderAsync} = require('./utils');
|
||||
|
||||
registerFixture('downloadsPath', async ({}, test) => {
|
||||
const downloadsPath = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
try {
|
||||
await test(downloadsPath);
|
||||
} finally {
|
||||
await removeFolderAsync(downloadsPath);
|
||||
}
|
||||
});
|
||||
|
||||
registerFixture('downloadsBrowser', async ({server, browserType, defaultBrowserOptions, downloadsPath}, test) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
const browser = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
downloadsPath: downloadsPath,
|
||||
});
|
||||
try {
|
||||
await test(browser);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
});
|
||||
|
||||
registerFixture('persistentDownloadsContext', async ({server, browserType, defaultBrowserOptions, downloadsPath}, test) => {
|
||||
const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
const context = await browserType.launchPersistentContext(
|
||||
userDataDir,
|
||||
{
|
||||
...defaultBrowserOptions,
|
||||
downloadsPath,
|
||||
acceptDownloads: true
|
||||
}
|
||||
);
|
||||
const page = context.pages()[0];
|
||||
page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
try {
|
||||
await test(context);
|
||||
} finally {
|
||||
await context.close();
|
||||
await state.context.close();
|
||||
await removeFolderAsync(userDataDir);
|
||||
}
|
||||
});
|
||||
|
||||
it('should keep downloadsPath folder', async({downloadsBrowser, downloadsPath, server}) => {
|
||||
const page = await downloadsBrowser.newPage();
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
await download.path().catch(e => error = e);
|
||||
await page.close();
|
||||
await downloadsBrowser.close();
|
||||
expect(fs.existsSync(downloadsPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should delete downloads when context closes', async({downloadsBrowser, downloadsPath, server}) => {
|
||||
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await page.close();
|
||||
expect(fs.existsSync(path)).toBeFalsy();
|
||||
|
||||
});
|
||||
it('should report downloads in downloadsPath folder', async({downloadsBrowser, downloadsPath, server}) => {
|
||||
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(path.startsWith(downloadsPath)).toBeTruthy();
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it('should accept downloads', async({persistentDownloadsContext, downloadsPath, server}) => {
|
||||
const page = persistentDownloadsContext.pages()[0];
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
const path = await download.path();
|
||||
expect(path.startsWith(downloadsPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not delete downloads when the context closes', async({persistentDownloadsContext}) => {
|
||||
const page = persistentDownloadsContext.pages()[0];
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
await persistentDownloadsContext.close();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
});
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 fs = require('fs');
|
||||
const os = require('os');
|
||||
const {mkdtempAsync, removeFolderAsync} = require('./utils');
|
||||
|
||||
registerFixture('downloadsPath', async ({}, test) => {
|
||||
const downloadsPath = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
try {
|
||||
await test(downloadsPath);
|
||||
} finally {
|
||||
await removeFolderAsync(downloadsPath);
|
||||
}
|
||||
});
|
||||
|
||||
registerFixture('downloadsBrowser', async ({server, browserType, defaultBrowserOptions, downloadsPath}, test) => {
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
const browser = await browserType.launch({
|
||||
...defaultBrowserOptions,
|
||||
downloadsPath: downloadsPath,
|
||||
});
|
||||
try {
|
||||
await test(browser);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
});
|
||||
|
||||
registerFixture('persistentDownloadsContext', async ({server, browserType, defaultBrowserOptions, downloadsPath}, test) => {
|
||||
const userDataDir = await mkdtempAsync(path.join(os.tmpdir(), 'playwright-test-'));
|
||||
server.setRoute('/download', (req, res) => {
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('Content-Disposition', 'attachment; filename=file.txt');
|
||||
res.end(`Hello world`);
|
||||
});
|
||||
const context = await browserType.launchPersistentContext(
|
||||
userDataDir,
|
||||
{
|
||||
...defaultBrowserOptions,
|
||||
downloadsPath,
|
||||
acceptDownloads: true
|
||||
}
|
||||
);
|
||||
const page = context.pages()[0];
|
||||
page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
try {
|
||||
await test(context);
|
||||
} finally {
|
||||
await context.close();
|
||||
await state.context.close();
|
||||
await removeFolderAsync(userDataDir);
|
||||
}
|
||||
});
|
||||
|
||||
describe('browserType.launch({downloadsPath})', function() {
|
||||
it('should keep downloadsPath folder', async({downloadsBrowser, downloadsPath, server}) => {
|
||||
const page = await downloadsBrowser.newPage();
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
await download.path().catch(e => error = e);
|
||||
await page.close();
|
||||
await downloadsBrowser.close();
|
||||
expect(fs.existsSync(downloadsPath)).toBeTruthy();
|
||||
});
|
||||
it('should delete downloads when context closes', async({downloadsBrowser, downloadsPath, server}) => {
|
||||
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
await page.close();
|
||||
expect(fs.existsSync(path)).toBeFalsy();
|
||||
});
|
||||
it('should report downloads in downloadsPath folder', async({downloadsBrowser, downloadsPath, server}) => {
|
||||
const page = await downloadsBrowser.newPage({ acceptDownloads: true });
|
||||
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
expect(path.startsWith(downloadsPath)).toBeTruthy();
|
||||
await page.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('browserType.launchPersistent({acceptDownloads})', function() {
|
||||
it('should accept downloads', async({persistentDownloadsContext, downloadsPath, server}) => {
|
||||
const page = persistentDownloadsContext.pages()[0];
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
expect(download.url()).toBe(`${server.PREFIX}/download`);
|
||||
expect(download.suggestedFilename()).toBe(`file.txt`);
|
||||
const path = await download.path();
|
||||
expect(path.startsWith(downloadsPath)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not delete downloads when the context closes', async({persistentDownloadsContext}) => {
|
||||
const page = persistentDownloadsContext.pages()[0];
|
||||
const [ download ] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.click('a')
|
||||
]);
|
||||
const path = await download.path();
|
||||
await persistentDownloadsContext.close();
|
||||
expect(fs.existsSync(path)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
171
test/emulation-focus.spec.js
Normal file
171
test/emulation-focus.spec.js
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it('should think that it is focused by default', async({page}) => {
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
});
|
||||
|
||||
it('should think that all pages are focused', async({page}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
expect(await page2.evaluate('document.hasFocus()')).toBe(true);
|
||||
await page2.close();
|
||||
});
|
||||
|
||||
it('should focus popups by default', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate('document.hasFocus()')).toBe(true);
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
});
|
||||
|
||||
it('should provide target for keyboard events', async({page, server}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
await Promise.all([
|
||||
page.goto(server.PREFIX + '/input/textarea.html'),
|
||||
page2.goto(server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.focus('input'),
|
||||
page2.focus('input'),
|
||||
]);
|
||||
const text = 'first';
|
||||
const text2 = 'second';
|
||||
await Promise.all([
|
||||
page.keyboard.type(text),
|
||||
page2.keyboard.type(text2),
|
||||
]);
|
||||
const results = await Promise.all([
|
||||
page.evaluate('result'),
|
||||
page2.evaluate('result'),
|
||||
]);
|
||||
expect(results).toEqual([text, text2]);
|
||||
});
|
||||
|
||||
it('should not affect mouse event target page', async({page, server}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
function clickCounter() {
|
||||
document.onclick = () => window.clickCount = (window.clickCount || 0) + 1;
|
||||
}
|
||||
await Promise.all([
|
||||
page.evaluate(clickCounter),
|
||||
page2.evaluate(clickCounter),
|
||||
page.focus('body'),
|
||||
page2.focus('body'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.mouse.click(1, 1),
|
||||
page2.mouse.click(1, 1),
|
||||
]);
|
||||
const counters = await Promise.all([
|
||||
page.evaluate('window.clickCount'),
|
||||
page2.evaluate('window.clickCount'),
|
||||
]);
|
||||
expect(counters ).toEqual([1,1]);
|
||||
});
|
||||
|
||||
it('should change document.activeElement', async({page, server}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
await Promise.all([
|
||||
page.goto(server.PREFIX + '/input/textarea.html'),
|
||||
page2.goto(server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.focus('input'),
|
||||
page2.focus('textarea'),
|
||||
]);
|
||||
const active = await Promise.all([
|
||||
page.evaluate('document.activeElement.tagName'),
|
||||
page2.evaluate('document.activeElement.tagName'),
|
||||
]);
|
||||
expect(active).toEqual(['INPUT', 'TEXTAREA']);
|
||||
});
|
||||
|
||||
it.skip(FFOX && !HEADLESS)('should not affect screenshots', async({page, server}) => {
|
||||
// Firefox headful produces a different image.
|
||||
const page2 = await page.context().newPage();
|
||||
await Promise.all([
|
||||
page.setViewportSize({width: 500, height: 500}),
|
||||
page.goto(server.PREFIX + '/grid.html'),
|
||||
page2.setViewportSize({width: 50, height: 50}),
|
||||
page2.goto(server.PREFIX + '/grid.html'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.focus('body'),
|
||||
page2.focus('body'),
|
||||
]);
|
||||
const screenshots = await Promise.all([
|
||||
page.screenshot(),
|
||||
page2.screenshot(),
|
||||
]);
|
||||
expect(screenshots[0]).toBeGolden('screenshot-sanity.png');
|
||||
expect(screenshots[1]).toBeGolden('grid-cell-0.png');
|
||||
});
|
||||
|
||||
it('should change focused iframe', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [frame1, frame2] = await Promise.all([
|
||||
utils.attachFrame(page, 'frame1', server.PREFIX + '/input/textarea.html'),
|
||||
utils.attachFrame(page, 'frame2', server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
function logger() {
|
||||
self._events = [];
|
||||
const element = document.querySelector('input');
|
||||
element.onfocus = element.onblur = (e) => self._events.push(e.type);
|
||||
}
|
||||
await Promise.all([
|
||||
frame1.evaluate(logger),
|
||||
frame2.evaluate(logger),
|
||||
]);
|
||||
const focused = await Promise.all([
|
||||
frame1.evaluate('document.hasFocus()'),
|
||||
frame2.evaluate('document.hasFocus()'),
|
||||
]);
|
||||
expect(focused).toEqual([false, false]);
|
||||
{
|
||||
await frame1.focus('input');
|
||||
const events = await Promise.all([
|
||||
frame1.evaluate('self._events'),
|
||||
frame2.evaluate('self._events'),
|
||||
]);
|
||||
expect(events).toEqual([['focus'], []]);
|
||||
const focused = await Promise.all([
|
||||
frame1.evaluate('document.hasFocus()'),
|
||||
frame2.evaluate('document.hasFocus()'),
|
||||
]);
|
||||
expect(focused).toEqual([true, false]);
|
||||
}
|
||||
{
|
||||
await frame2.focus('input');
|
||||
const events = await Promise.all([
|
||||
frame1.evaluate('self._events'),
|
||||
frame2.evaluate('self._events'),
|
||||
]);
|
||||
expect(events).toEqual([['focus', 'blur'], ['focus']]);
|
||||
const focused = await Promise.all([
|
||||
frame1.evaluate('document.hasFocus()'),
|
||||
frame2.evaluate('document.hasFocus()'),
|
||||
]);
|
||||
expect(focused).toEqual([false, true]);
|
||||
}
|
||||
});
|
||||
|
|
@ -1,690 +0,0 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
describe('BrowserContext({viewport})', function() {
|
||||
it('should get the proper default viewport size', async({page, server}) => {
|
||||
await utils.verifyViewport(page, 1280, 720);
|
||||
});
|
||||
it('should set the proper viewport size', async({page, server}) => {
|
||||
await utils.verifyViewport(page, 1280, 720);
|
||||
await page.setViewportSize({width: 123, height: 456});
|
||||
await utils.verifyViewport(page, 123, 456);
|
||||
});
|
||||
it('should return correct outerWidth and outerHeight', async({page}) => {
|
||||
const size = await page.evaluate(() => {
|
||||
return {
|
||||
innerWidth: window.innerWidth,
|
||||
innerHeight: window.innerHeight,
|
||||
outerWidth: window.outerWidth,
|
||||
outerHeight: window.outerHeight,
|
||||
};
|
||||
});
|
||||
expect(size.innerWidth).toBe(1280);
|
||||
expect(size.innerHeight).toBe(720);
|
||||
expect(size.outerWidth >= size.innerWidth).toBeTruthy();
|
||||
expect(size.outerHeight >= size.innerHeight).toBeTruthy();
|
||||
});
|
||||
it('should emulate device width', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.width)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-width: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-width: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-width: 500px)').matches)).toBe(true);
|
||||
});
|
||||
it('should emulate device height', async({page, server}) => {
|
||||
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
|
||||
await page.setViewportSize({width: 200, height: 200});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(200);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 100px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 300px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 100px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 300px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(true);
|
||||
await page.setViewportSize({width: 500, height: 500});
|
||||
expect(await page.evaluate(() => window.screen.height)).toBe(500);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 400px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(min-device-height: 600px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 400px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(max-device-height: 600px)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 200px)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(device-height: 500px)').matches)).toBe(true);
|
||||
});
|
||||
it('should not have touch by default', async({page, server}) => {
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(false);
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('NO');
|
||||
});
|
||||
it('should support touch with null viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: null, hasTouch: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should report null viewportSize when given null viewport', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: null });
|
||||
const page = await context.newPage();
|
||||
expect(page.viewportSize()).toBe(null);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(FFOX)('viewport.isMobile', () => {
|
||||
// Firefox does not support isMobile.
|
||||
it('should support mobile emulation', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(400);
|
||||
await context.close();
|
||||
});
|
||||
it('should support touch emulation', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true);
|
||||
expect(await page.evaluate(dispatchTouch)).toBe('Received touch');
|
||||
await context.close();
|
||||
|
||||
function dispatchTouch() {
|
||||
let fulfill;
|
||||
const promise = new Promise(x => fulfill = x);
|
||||
window.ontouchstart = function(e) {
|
||||
fulfill('Received touch');
|
||||
};
|
||||
window.dispatchEvent(new Event('touchstart'));
|
||||
|
||||
fulfill('Did not receive touch');
|
||||
|
||||
return promise;
|
||||
}
|
||||
});
|
||||
it('should be detectable by Modernizr', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/detect-touch.html');
|
||||
expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES');
|
||||
await context.close();
|
||||
});
|
||||
it('should detect touch when applying viewport with touches', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.addScriptTag({url: server.PREFIX + '/modernizr.js'});
|
||||
expect(await page.evaluate(() => Modernizr.touchevents)).toBe(true);
|
||||
await context.close();
|
||||
});
|
||||
it('should support landscape emulation', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const iPhoneLandscape = playwright.devices['iPhone 6 landscape'];
|
||||
const context1 = await browser.newContext({ ...iPhone });
|
||||
const page1 = await context1.newPage();
|
||||
await page1.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false);
|
||||
const context2 = await browser.newContext({ ...iPhoneLandscape });
|
||||
const page2 = await context2.newPage();
|
||||
expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true);
|
||||
await context1.close();
|
||||
await context2.close();
|
||||
});
|
||||
it('should support window.orientation emulation', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.orientation)).toBe(0);
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect(await page.evaluate(() => window.orientation)).toBe(90);
|
||||
await context.close();
|
||||
});
|
||||
it('should fire orientationchange event', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
await page.evaluate(() => {
|
||||
window.counter = 0;
|
||||
window.addEventListener('orientationchange', () => console.log(++window.counter));
|
||||
});
|
||||
|
||||
const event1 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 400, height: 300});
|
||||
expect((await event1).text()).toBe('1');
|
||||
|
||||
const event2 = page.waitForEvent('console');
|
||||
await page.setViewportSize({width: 300, height: 400});
|
||||
expect((await event2).text()).toBe('2');
|
||||
await context.close();
|
||||
});
|
||||
it('default mobile viewports to 980 width', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/empty.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
|
||||
await context.close();
|
||||
});
|
||||
it('respect meta viewport tag', async({browser, server}) => {
|
||||
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(320);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip(FFOX)('Page.emulate', function() {
|
||||
it('should work', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/mobile.html');
|
||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||
await context.close();
|
||||
});
|
||||
it('should support clicking', async({playwright, browser, server}) => {
|
||||
const iPhone = playwright.devices['iPhone 6'];
|
||||
const context = await browser.newContext({ ...iPhone });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/button.html');
|
||||
const button = await page.$('button');
|
||||
await page.evaluate(button => button.style.marginTop = '200px', button);
|
||||
await button.click();
|
||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||
await context.close();
|
||||
});
|
||||
it('should scroll to click', async({browser, server}) => {
|
||||
const context = await browser.newContext({
|
||||
viewport: {
|
||||
width: 400,
|
||||
height: 400,
|
||||
},
|
||||
deviceScaleFactor: 1,
|
||||
isMobile: true
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.PREFIX + '/input/scrollable.html');
|
||||
const element = await page.$('#button-91');
|
||||
await element.click();
|
||||
expect(await element.textContent()).toBe('clicked');
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.emulateMedia type', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
await page.emulateMedia({ media: 'print' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({});
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({ media: null });
|
||||
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 type argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ media: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toContain('media: expected one of (screen|print|null)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Page.emulateMedia colorScheme', function() {
|
||||
it('should work', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
});
|
||||
it('should default to light', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: null });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
});
|
||||
it('should throw in case of bad argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ colorScheme: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toContain('colorScheme: expected one of (dark|light|no-preference|null)');
|
||||
});
|
||||
it('should work during navigation', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
const navigated = page.goto(server.EMPTY_PAGE);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
await Promise.all([
|
||||
page.emulateMedia({ colorScheme: ['dark', 'light'][i & 1] }),
|
||||
new Promise(f => setTimeout(f, 1)),
|
||||
]);
|
||||
}
|
||||
await navigated;
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
it('should work in popup', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ colorScheme: 'dark' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const page = await browser.newPage({ colorScheme: 'light' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
await page.close();
|
||||
}
|
||||
});
|
||||
it('should work in cross-process iframe', async({browser, server}) => {
|
||||
const page = await browser.newPage({ colorScheme: 'dark' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
expect(await frame.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await page.close();
|
||||
});
|
||||
it.fail(FFOX)('should change the actual colors in css', async({page}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
div {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
div {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<div>Hello</div>
|
||||
`);
|
||||
function getBackgroundColor() {
|
||||
return page.$eval('div', div => window.getComputedStyle(div).backgroundColor);
|
||||
}
|
||||
|
||||
await page.emulateMedia({ colorScheme: "light" });
|
||||
expect(await getBackgroundColor()).toBe('rgb(255, 255, 255)');
|
||||
|
||||
await page.emulateMedia({ colorScheme: "dark" });
|
||||
expect(await getBackgroundColor()).toBe('rgb(0, 0, 0)');
|
||||
|
||||
await page.emulateMedia({ colorScheme: "light" });
|
||||
expect(await getBackgroundColor()).toBe('rgb(255, 255, 255)');
|
||||
})
|
||||
});
|
||||
|
||||
describe('BrowserContext({timezoneId})', function() {
|
||||
it('should work', async ({ browser }) => {
|
||||
const func = () => new Date(1479579154987).toString();
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Jamaica' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Pacific/Honolulu' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'America/Buenos_Aires' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw for invalid timezone IDs when creating pages', async({browser}) => {
|
||||
for (const timezoneId of ['Foo/Bar', 'Baz/Qux']) {
|
||||
let error = null;
|
||||
const context = await browser.newContext({ timezoneId });
|
||||
const page = await context.newPage().catch(e => error = e);
|
||||
expect(error.message).toContain(`Invalid timezone ID: ${timezoneId}`);
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ timezoneId: 'Europe/Moscow' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('BrowserContext({locale})', function() {
|
||||
it('should affect accept-language header', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
const [request] = await Promise.all([
|
||||
server.waitForRequest('/empty.html'),
|
||||
page.goto(server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(request.headers['accept-language'].substr(0, 5)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should affect navigator.language', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
expect(await page.evaluate(() => navigator.language)).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should format number', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString())).toBe('1,000,000.5');
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => (1000000.50).toLocaleString().replace(/\s/g, ' '))).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should format date', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'en-US', timezoneId: 'America/Los_Angeles' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const formatted = 'Sat Nov 19 2016 10:12:34 GMT-0800 (Pacific Standard Time)';
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(formatted);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({ locale: 'de-DE', timezoneId: 'Europe/Berlin' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
expect(await page.evaluate(() => new Date(1479579154987).toString())).toBe(
|
||||
'Sat Nov 19 2016 19:12:34 GMT+0100 (Mitteleuropäische Normalzeit)');
|
||||
await context.close();
|
||||
}
|
||||
});
|
||||
it('should format number in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.result);
|
||||
expect(result).toBe('1 000 000,5');
|
||||
await context.close();
|
||||
});
|
||||
it('should affect navigator.language in popups', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'fr-CH' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => window._popup = window.open(url), server.PREFIX + '/formatted-number.html'),
|
||||
]);
|
||||
await popup.waitForLoadState('domcontentloaded');
|
||||
const result = await popup.evaluate(() => window.initialNavigatorLanguage);
|
||||
expect(result).toBe('fr-CH');
|
||||
await context.close();
|
||||
});
|
||||
it('should work for multiple pages sharing same process', async({browser, server}) => {
|
||||
const context = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
[popup] = await Promise.all([
|
||||
popup.waitForEvent('popup'),
|
||||
popup.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
await context.close();
|
||||
});
|
||||
it('should be isolated between contexts', async({browser, server}) => {
|
||||
const context1 = await browser.newContext({ locale: 'en-US' });
|
||||
const promises = [];
|
||||
// By default firefox limits number of child web processes to 8.
|
||||
for (let i = 0; i< 8; i++)
|
||||
promises.push(context1.newPage());
|
||||
await Promise.all(promises);
|
||||
|
||||
const context2 = await browser.newContext({ locale: 'ru-RU' });
|
||||
const page2 = await context2.newPage();
|
||||
|
||||
const localeNumber = () => (1000000.50).toLocaleString();
|
||||
const numbers = await Promise.all(context1.pages().map(page => page.evaluate(localeNumber)));
|
||||
|
||||
numbers.forEach(value => expect(value).toBe('1,000,000.5'));
|
||||
expect(await page2.evaluate(localeNumber)).toBe('1 000 000,5');
|
||||
|
||||
await Promise.all([
|
||||
context1.close(),
|
||||
context2.close()
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('focus', function() {
|
||||
it('should think that it is focused by default', async({page}) => {
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
});
|
||||
it('should think that all pages are focused', async({page}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
expect(await page2.evaluate('document.hasFocus()')).toBe(true);
|
||||
await page2.close();
|
||||
});
|
||||
it('should focus popups by default', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate('document.hasFocus()')).toBe(true);
|
||||
expect(await page.evaluate('document.hasFocus()')).toBe(true);
|
||||
});
|
||||
it('should provide target for keyboard events', async({page, server}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
await Promise.all([
|
||||
page.goto(server.PREFIX + '/input/textarea.html'),
|
||||
page2.goto(server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.focus('input'),
|
||||
page2.focus('input'),
|
||||
]);
|
||||
const text = 'first';
|
||||
const text2 = 'second';
|
||||
await Promise.all([
|
||||
page.keyboard.type(text),
|
||||
page2.keyboard.type(text2),
|
||||
]);
|
||||
const results = await Promise.all([
|
||||
page.evaluate('result'),
|
||||
page2.evaluate('result'),
|
||||
]);
|
||||
expect(results).toEqual([text, text2]);
|
||||
});
|
||||
it('should not affect mouse event target page', async({page, server}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
function clickCounter() {
|
||||
document.onclick = () => window.clickCount = (window.clickCount || 0) + 1;
|
||||
}
|
||||
await Promise.all([
|
||||
page.evaluate(clickCounter),
|
||||
page2.evaluate(clickCounter),
|
||||
page.focus('body'),
|
||||
page2.focus('body'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.mouse.click(1, 1),
|
||||
page2.mouse.click(1, 1),
|
||||
]);
|
||||
const counters = await Promise.all([
|
||||
page.evaluate('window.clickCount'),
|
||||
page2.evaluate('window.clickCount'),
|
||||
]);
|
||||
expect(counters ).toEqual([1,1]);
|
||||
});
|
||||
it('should change document.activeElement', async({page, server}) => {
|
||||
const page2 = await page.context().newPage();
|
||||
await Promise.all([
|
||||
page.goto(server.PREFIX + '/input/textarea.html'),
|
||||
page2.goto(server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.focus('input'),
|
||||
page2.focus('textarea'),
|
||||
]);
|
||||
const active = await Promise.all([
|
||||
page.evaluate('document.activeElement.tagName'),
|
||||
page2.evaluate('document.activeElement.tagName'),
|
||||
]);
|
||||
expect(active).toEqual(['INPUT', 'TEXTAREA']);
|
||||
});
|
||||
it.skip(FFOX && !HEADLESS)('should not affect screenshots', async({page, server}) => {
|
||||
// Firefox headful produces a different image.
|
||||
const page2 = await page.context().newPage();
|
||||
await Promise.all([
|
||||
page.setViewportSize({width: 500, height: 500}),
|
||||
page.goto(server.PREFIX + '/grid.html'),
|
||||
page2.setViewportSize({width: 50, height: 50}),
|
||||
page2.goto(server.PREFIX + '/grid.html'),
|
||||
]);
|
||||
await Promise.all([
|
||||
page.focus('body'),
|
||||
page2.focus('body'),
|
||||
]);
|
||||
const screenshots = await Promise.all([
|
||||
page.screenshot(),
|
||||
page2.screenshot(),
|
||||
]);
|
||||
expect(screenshots[0]).toBeGolden('screenshot-sanity.png');
|
||||
expect(screenshots[1]).toBeGolden('grid-cell-0.png');
|
||||
});
|
||||
it('should change focused iframe', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [frame1, frame2] = await Promise.all([
|
||||
utils.attachFrame(page, 'frame1', server.PREFIX + '/input/textarea.html'),
|
||||
utils.attachFrame(page, 'frame2', server.PREFIX + '/input/textarea.html'),
|
||||
]);
|
||||
function logger() {
|
||||
self._events = [];
|
||||
const element = document.querySelector('input');
|
||||
element.onfocus = element.onblur = (e) => self._events.push(e.type);
|
||||
}
|
||||
await Promise.all([
|
||||
frame1.evaluate(logger),
|
||||
frame2.evaluate(logger),
|
||||
]);
|
||||
const focused = await Promise.all([
|
||||
frame1.evaluate('document.hasFocus()'),
|
||||
frame2.evaluate('document.hasFocus()'),
|
||||
]);
|
||||
expect(focused).toEqual([false, false]);
|
||||
{
|
||||
await frame1.focus('input');
|
||||
const events = await Promise.all([
|
||||
frame1.evaluate('self._events'),
|
||||
frame2.evaluate('self._events'),
|
||||
]);
|
||||
expect(events).toEqual([['focus'], []]);
|
||||
const focused = await Promise.all([
|
||||
frame1.evaluate('document.hasFocus()'),
|
||||
frame2.evaluate('document.hasFocus()'),
|
||||
]);
|
||||
expect(focused).toEqual([true, false]);
|
||||
}
|
||||
{
|
||||
await frame2.focus('input');
|
||||
const events = await Promise.all([
|
||||
frame1.evaluate('self._events'),
|
||||
frame2.evaluate('self._events'),
|
||||
]);
|
||||
expect(events).toEqual([['focus', 'blur'], ['focus']]);
|
||||
const focused = await Promise.all([
|
||||
frame1.evaluate('document.hasFocus()'),
|
||||
frame2.evaluate('document.hasFocus()'),
|
||||
]);
|
||||
expect(focused).toEqual([false, true]);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
148
test/page-emulate-media.spec.js
Normal file
148
test/page-emulate-media.spec.js
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* Copyright 2018 Google Inc. All rights reserved.
|
||||
* Modifications copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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 utils = require('./utils');
|
||||
const {CHROMIUM, FFOX, MAC, HEADLESS} = testOptions;
|
||||
|
||||
it('should emulate type', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(false);
|
||||
await page.emulateMedia({ media: 'print' });
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({});
|
||||
expect(await page.evaluate(() => matchMedia('screen').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('print').matches)).toBe(true);
|
||||
await page.emulateMedia({ media: null });
|
||||
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 type argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ media: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toContain('media: expected one of (screen|print|null)');
|
||||
});
|
||||
|
||||
it('should emulate scheme work', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
});
|
||||
|
||||
it('should default to light', async({page, server}) => {
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
|
||||
await page.emulateMedia({ colorScheme: null });
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw in case of bad argument', async({page, server}) => {
|
||||
let error = null;
|
||||
await page.emulateMedia({ colorScheme: 'bad' }).catch(e => error = e);
|
||||
expect(error.message).toContain('colorScheme: expected one of (dark|light|no-preference|null)');
|
||||
});
|
||||
|
||||
it('should work during navigation', async({page, server}) => {
|
||||
await page.emulateMedia({ colorScheme: 'light' });
|
||||
const navigated = page.goto(server.EMPTY_PAGE);
|
||||
for (let i = 0; i < 9; i++) {
|
||||
await Promise.all([
|
||||
page.emulateMedia({ colorScheme: ['dark', 'light'][i & 1] }),
|
||||
new Promise(f => setTimeout(f, 1)),
|
||||
]);
|
||||
}
|
||||
await navigated;
|
||||
expect(await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
});
|
||||
|
||||
it('should work in popup', async({browser, server}) => {
|
||||
{
|
||||
const context = await browser.newContext({ colorScheme: 'dark' });
|
||||
const page = await context.newPage();
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(false);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const page = await browser.newPage({ colorScheme: 'light' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const [popup] = await Promise.all([
|
||||
page.waitForEvent('popup'),
|
||||
page.evaluate(url => { window.open(url); }, server.EMPTY_PAGE),
|
||||
]);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches)).toBe(true);
|
||||
expect(await popup.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
|
||||
await page.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should work in cross-process iframe', async({browser, server}) => {
|
||||
const page = await browser.newPage({ colorScheme: 'dark' });
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await utils.attachFrame(page, 'frame1', server.CROSS_PROCESS_PREFIX + '/empty.html');
|
||||
const frame = page.frames()[1];
|
||||
expect(await frame.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
|
||||
await page.close();
|
||||
});
|
||||
|
||||
it.fail(FFOX)('should change the actual colors in css', async({page}) => {
|
||||
await page.setContent(`
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
div {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
div {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<div>Hello</div>
|
||||
`);
|
||||
function getBackgroundColor() {
|
||||
return page.$eval('div', div => window.getComputedStyle(div).backgroundColor);
|
||||
}
|
||||
|
||||
await page.emulateMedia({ colorScheme: "light" });
|
||||
expect(await getBackgroundColor()).toBe('rgb(255, 255, 255)');
|
||||
|
||||
await page.emulateMedia({ colorScheme: "dark" });
|
||||
expect(await getBackgroundColor()).toBe('rgb(0, 0, 0)');
|
||||
|
||||
await page.emulateMedia({ colorScheme: "light" });
|
||||
expect(await getBackgroundColor()).toBe('rgb(255, 255, 255)');
|
||||
})
|
||||
Loading…
Reference in a new issue