test: remove describes (5) (#3294)

This commit is contained in:
Pavel Feldman 2020-08-04 15:57:25 -07:00 committed by GitHub
parent 1673e62779
commit 4cbfa09c2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1535 additions and 1397 deletions

View File

@ -0,0 +1,38 @@
/**
* 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 path = require('path');
const fs = require('fs');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
it('browserType.executablePath should work', async({browserType}) => {
const executablePath = browserType.executablePath();
expect(fs.existsSync(executablePath)).toBe(true);
expect(fs.realpathSync(executablePath)).toBe(executablePath);
});
it('browserType.name should work', async({browserType}) => {
if (WEBKIT)
expect(browserType.name()).toBe('webkit');
else if (FFOX)
expect(browserType.name()).toBe('firefox');
else if (CHROMIUM)
expect(browserType.name()).toBe('chromium');
else
throw new Error('Unknown browser');
});

View File

@ -0,0 +1,73 @@
/**
* 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 path = require('path');
const fs = require('fs');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
it.slow()('should be able to reconnect to a browser', async({browserType, defaultBrowserOptions, server}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
{
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browserContext = await browser.newContext();
const page = await browserContext.newPage();
await page.goto(server.EMPTY_PAGE);
await browser.close();
}
{
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browserContext = await browser.newContext();
const page = await browserContext.newPage();
await page.goto(server.EMPTY_PAGE);
await browser.close();
}
await browserServer._checkLeaks();
await browserServer.close();
});
it.fail(USES_HOOKS || (CHROMIUM && WIN)).slow()('should handle exceptions during connect', async({browserType, defaultBrowserOptions, server}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const __testHookBeforeCreateBrowser = () => { throw new Error('Dummy') };
const error = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint(), __testHookBeforeCreateBrowser }).catch(e => e);
await browserServer._checkLeaks();
await browserServer.close();
expect(error.message).toContain('Dummy');
});
it('should set the browser connected state', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
expect(remote.isConnected()).toBe(true);
await remote.close();
expect(remote.isConnected()).toBe(false);
await browserServer._checkLeaks();
await browserServer.close();
});
it('should throw when used after isConnected returns false', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
await Promise.all([
browserServer.close(),
new Promise(f => remote.once('disconnected', f)),
]);
expect(remote.isConnected()).toBe(false);
const error = await page.evaluate('1 + 1').catch(e => e);
expect(error.message).toContain('has been closed');
});

View File

@ -0,0 +1,149 @@
/**
* 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 path = require('path');
const fs = require('fs');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
it('should work', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browserContext = await browser.newContext();
expect(browserContext.pages().length).toBe(0);
expect(browserServer.wsEndpoint()).not.toBe(null);
const page = await browserContext.newPage();
expect(await page.evaluate('11 * 11')).toBe(121);
await page.close();
await browser.close();
await browserServer._checkLeaks();
await browserServer.close();
});
it('should fire "disconnected" when closing the server', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
const closedPromise = new Promise(f => browserServer.on('close', f));
browserServer.kill();
await Promise.all([
disconnectedEventPromise,
closedPromise,
]);
});
it('should fire "close" event during kill', async({browserType, defaultBrowserOptions}) => {
const order = [];
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const closedPromise = new Promise(f => browserServer.on('close', () => {
order.push('closed');
f();
}));
await Promise.all([
browserServer.kill().then(() => order.push('killed')),
closedPromise,
]);
expect(order).toEqual(['closed', 'killed']);
});
it('should return child_process instance', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
expect(browserServer.process().pid).toBeGreaterThan(0);
await browserServer.close();
});
it('should fire close event', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const [result] = await Promise.all([
new Promise(f => browserServer.on('close', (exitCode, signal) => f({ exitCode, signal }))),
browserServer.close(),
]);
expect(result.exitCode).toBe(0);
expect(result.signal).toBe(null);
});
it('should reject navigation when browser closes', async({browserType, defaultBrowserOptions, server}) => {
server.setRoute('/one-style.css', () => {});
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
const navigationPromise = page.goto(server.PREFIX + '/one-style.html', {timeout: 60000}).catch(e => e);
await server.waitForRequest('/one-style.css');
await remote.close();
const error = await navigationPromise;
expect(error.message).toContain('Navigation failed because page was closed!');
await browserServer._checkLeaks();
await browserServer.close();
});
it('should reject waitForSelector when browser closes', async({browserType, defaultBrowserOptions, server}) => {
server.setRoute('/empty.html', () => {});
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
const watchdog = page.waitForSelector('div', { state: 'attached', timeout: 60000 }).catch(e => e);
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
await page.waitForSelector('body', { state: 'attached' });
await remote.close();
const error = await watchdog;
expect(error.message).toContain('Protocol error');
await browserServer._checkLeaks();
await browserServer.close();
});
it('should throw if used after disconnect', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
await remote.close();
const error = await page.evaluate('1 + 1').catch(e => e);
expect(error.message).toContain('has been closed');
await browserServer._checkLeaks();
await browserServer.close();
});
it('should emit close events on pages and contexts', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const context = await remote.newContext();
const page = await context.newPage();
let pageClosed = false;
page.on('close', e => pageClosed = true);
await Promise.all([
new Promise(f => context.on('close', f)),
browserServer.close()
]);
expect(pageClosed).toBeTruthy();
});
it('should terminate network waiters', async({browserType, defaultBrowserOptions, server}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const newPage = await remote.newPage();
const results = await Promise.all([
newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e),
newPage.waitForResponse(server.EMPTY_PAGE).catch(e => e),
browserServer.close()
]);
for (let i = 0; i < 2; i++) {
const message = results[i].message;
expect(message).toContain('Page closed');
expect(message).not.toContain('Timeout');
}
});

View File

@ -0,0 +1,106 @@
/**
* 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 path = require('path');
const fs = require('fs');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
it('should reject all promises when browser is closed', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
const page = await (await browser.newContext()).newPage();
let error = null;
const neverResolves = page.evaluate(() => new Promise(r => {})).catch(e => error = e);
await page.evaluate(() => new Promise(f => setTimeout(f, 0)));
await browser.close();
await neverResolves;
expect(error.message).toContain('Protocol error');
});
it('should throw if userDataDir option is passed', async({browserType, defaultBrowserOptions}) => {
let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, {userDataDir: 'random-path'});
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('launchPersistentContext');
});
it.skip(FFOX)('should throw if page argument is passed', async({browserType, defaultBrowserOptions}) => {
let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] });
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('can not specify page');
});
it.fail(true)('should reject if launched browser fails immediately', async({browserType, defaultBrowserOptions}) => {
// I'm getting ENCONRESET on this one.
const options = Object.assign({}, defaultBrowserOptions, {executablePath: path.join(__dirname, 'assets', 'dummy_bad_browser_executable.js')});
let waitError = null;
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('== logs ==');
});
it('should reject if executable path is invalid', async({browserType, defaultBrowserOptions}) => {
let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'});
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('Failed to launch');
});
it.skip(USES_HOOKS)('should handle timeout', async({browserType, defaultBrowserOptions}) => {
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain(`browserType.launch: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`[browser] <launching>`);
expect(error.message).toContain(`[browser] <launched> pid=`);
});
it.skip(USES_HOOKS)('should handle exception', async({browserType, defaultBrowserOptions}) => {
const e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 };
const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain('Dummy');
});
it.skip(USES_HOOKS)('should report launch log', async({browserType, defaultBrowserOptions}) => {
const e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 };
const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain('<launching>');
});
it.slow()('should accept objects as options', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch({ ...defaultBrowserOptions, process });
await browser.close();
});
it('should fire close event for all contexts', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
const context = await browser.newContext();
let closed = false;
context.on('close', () => closed = true);
await browser.close();
expect(closed).toBe(true);
});
it('should be callable twice', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
await Promise.all([
browser.close(),
browser.close(),
]);
await browser.close();
});

View File

@ -1,88 +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, CHANNEL} = testOptions;
describe.skip(!CHROMIUM)('Service Worker', function() {
it('should create a worker from a service worker', async({page, server, context}) => {
const [worker] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
]);
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
});
it('serviceWorkers() should return current workers', async({page, server, context}) => {
const [worker1] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
]);
let workers = context.serviceWorkers();
expect(workers.length).toBe(1);
const [worker2] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html')
]);
workers = context.serviceWorkers();
expect(workers.length).toBe(2);
expect(workers).toContain(worker1);
expect(workers).toContain(worker2);
});
it('should not create a worker from a shared worker', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
let serviceWorkerCreated;
context.once('serviceworker', () => serviceWorkerCreated = true);
await page.evaluate(() => {
new SharedWorker('data:text/javascript,console.log("hi")');
});
expect(serviceWorkerCreated).not.toBeTruthy();
});
it('should close service worker together with the context', async({browser, server}) => {
const context = await browser.newContext();
const page = await context.newPage();
const [worker] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
]);
const messages = [];
context.on('close', () => messages.push('context'));
worker.on('close', () => messages.push('worker'));
await context.close();
expect(messages.join('|')).toBe('worker|context');
});
});
describe.skip(!CHROMIUM)('Chromium-Specific Page Tests', function() {
it('Page.route should work with intervention headers', async({server, page}) => {
server.setRoute('/intervention', (req, res) => res.end(`
<script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
</script>
`));
server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => {
serverRequest = req;
res.end('console.log(1);');
});
await page.route('*', route => route.continue());
await page.goto(server.PREFIX + '/intervention');
// Check for feature URL substring rather than https://www.chromestatus.com to
// make it work with Edgium.
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
});
});

View File

@ -0,0 +1,87 @@
/**
* 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, CHANNEL} = testOptions;
it.skip(!CHROMIUM)('should create a worker from a service worker', async({page, server, context}) => {
const [worker] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
]);
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]');
});
it.skip(!CHROMIUM)('serviceWorkers() should return current workers', async({page, server, context}) => {
const [worker1] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
]);
let workers = context.serviceWorkers();
expect(workers.length).toBe(1);
const [worker2] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html')
]);
workers = context.serviceWorkers();
expect(workers.length).toBe(2);
expect(workers).toContain(worker1);
expect(workers).toContain(worker2);
});
it.skip(!CHROMIUM)('should not create a worker from a shared worker', async({page, server, context}) => {
await page.goto(server.EMPTY_PAGE);
let serviceWorkerCreated;
context.once('serviceworker', () => serviceWorkerCreated = true);
await page.evaluate(() => {
new SharedWorker('data:text/javascript,console.log("hi")');
});
expect(serviceWorkerCreated).not.toBeTruthy();
});
it.skip(!CHROMIUM)('should close service worker together with the context', async({browser, server}) => {
const context = await browser.newContext();
const page = await context.newPage();
const [worker] = await Promise.all([
context.waitForEvent('serviceworker'),
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
]);
const messages = [];
context.on('close', () => messages.push('context'));
worker.on('close', () => messages.push('worker'));
await context.close();
expect(messages.join('|')).toBe('worker|context');
});
it.skip(!CHROMIUM)('Page.route should work with intervention headers', async({server, page}) => {
server.setRoute('/intervention', (req, res) => res.end(`
<script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
</script>
`));
server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => {
serverRequest = req;
res.end('console.log(1);');
});
await page.route('*', route => route.continue());
await page.goto(server.PREFIX + '/intervention');
// Check for feature URL substring rather than https://www.chromestatus.com to
// make it work with Edgium.
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
});

View File

@ -1,90 +0,0 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const utils = require('../utils');
const {makeUserDataDir, removeUserDataDir} = utils;
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
describe.skip(!CHROMIUM)('launcher', function() {
it('should throw with remote-debugging-pipe argument', async({browserType, defaultBrowserOptions}) => {
const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
const error = await browserType.launchServer(options).catch(e => e);
expect(error.message).toContain('Playwright manages remote debugging connection itself');
});
it('should not throw with remote-debugging-port argument', async({browserType, defaultBrowserOptions}) => {
const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
const browser = await browserType.launchServer(options);
await browser.close();
});
it.fail(USES_HOOKS || WIN)('should open devtools when "devtools: true" option is given', async({browserType, defaultBrowserOptions}) => {
let devtoolsCallback;
const devtoolsPromise = new Promise(f => devtoolsCallback = f);
const __testHookForDevTools = devtools => devtools.__testHookOnBinding = parsed => {
if (parsed.method === 'getPreferences')
devtoolsCallback();
};
const browser = await browserType.launch({...defaultBrowserOptions, headless: false, devtools: true, __testHookForDevTools});
const context = await browser.newContext();
await Promise.all([
devtoolsPromise,
context.newPage()
]);
await browser.close();
});
});
describe.skip(!CHROMIUM)('extensions', () => {
it('should return background pages', async({browserType, defaultBrowserOptions}) => {
const userDataDir = await makeUserDataDir();
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
const extensionOptions = {...defaultBrowserOptions,
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
};
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
const backgroundPages = context.backgroundPages();
let backgroundPage = backgroundPages.length
? backgroundPages[0]
: await context.waitForEvent('backgroundpage');
expect(backgroundPage).toBeTruthy();
expect(context.backgroundPages()).toContain(backgroundPage);
expect(context.pages()).not.toContain(backgroundPage);
await context.close();
await removeUserDataDir(userDataDir);
});
});
describe.skip(!CHROMIUM)('BrowserContext', function() {
it('should not create pages automatically', async ({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
const browserSession = await browser.newBrowserCDPSession();
const targets = [];
browserSession.on('Target.targetCreated', async ({targetInfo}) => {
if (targetInfo.type !== 'browser')
targets.push(targetInfo);
});
await browserSession.send('Target.setDiscoverTargets', { discover: true });
await browser.newContext();
await browser.close();
expect(targets.length).toBe(0);
});
});

View File

@ -0,0 +1,86 @@
/**
* Copyright 2019 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const path = require('path');
const utils = require('../utils');
const {makeUserDataDir, removeUserDataDir} = utils;
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS} = testOptions;
it.skip(!CHROMIUM)('should throw with remote-debugging-pipe argument', async({browserType, defaultBrowserOptions}) => {
const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
const error = await browserType.launchServer(options).catch(e => e);
expect(error.message).toContain('Playwright manages remote debugging connection itself');
});
it.skip(!CHROMIUM)('should not throw with remote-debugging-port argument', async({browserType, defaultBrowserOptions}) => {
const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
const browser = await browserType.launchServer(options);
await browser.close();
});
it.skip(!CHROMIUM || USES_HOOKS || WIN)('should open devtools when "devtools: true" option is given', async({browserType, defaultBrowserOptions}) => {
let devtoolsCallback;
const devtoolsPromise = new Promise(f => devtoolsCallback = f);
const __testHookForDevTools = devtools => devtools.__testHookOnBinding = parsed => {
if (parsed.method === 'getPreferences')
devtoolsCallback();
};
const browser = await browserType.launch({...defaultBrowserOptions, headless: false, devtools: true, __testHookForDevTools});
const context = await browser.newContext();
await Promise.all([
devtoolsPromise,
context.newPage()
]);
await browser.close();
});
it.skip(!CHROMIUM)('should return background pages', async({browserType, defaultBrowserOptions}) => {
const userDataDir = await makeUserDataDir();
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
const extensionOptions = {...defaultBrowserOptions,
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
],
};
const context = await browserType.launchPersistentContext(userDataDir, extensionOptions);
const backgroundPages = context.backgroundPages();
let backgroundPage = backgroundPages.length
? backgroundPages[0]
: await context.waitForEvent('backgroundpage');
expect(backgroundPage).toBeTruthy();
expect(context.backgroundPages()).toContain(backgroundPage);
expect(context.pages()).not.toContain(backgroundPage);
await context.close();
await removeUserDataDir(userDataDir);
});
it.skip(!CHROMIUM)('should not create pages automatically', async ({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
const browserSession = await browser.newBrowserCDPSession();
const targets = [];
browserSession.on('Target.targetCreated', async ({targetInfo}) => {
if (targetInfo.type !== 'browser')
targets.push(targetInfo);
});
await browserSession.send('Target.setDiscoverTargets', { discover: true });
await browser.newContext();
await browser.close();
expect(targets.length).toBe(0);
});

View File

@ -1,350 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
registerFixture('sppBrowser', async ({browserType, defaultBrowserOptions}, test) => {
const browser = await browserType.launch({
...defaultBrowserOptions,
args: (defaultBrowserOptions.args || []).concat(['--site-per-process'])
});
try {
await test(browser);
} finally {
await browser.close();
}
});
registerFixture('sppContext', async ({sppBrowser}, test) => {
const context = await sppBrowser.newContext();
try {
await test(context);
} finally {
await context.close();
}
});
registerFixture('sppPage', async ({sppContext}, test) => {
const page = await sppContext.newPage();
await test(page);
});
describe.skip(!CHROMIUM)('OOPIF', function() {
it('should report oopif frames', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
});
it('should handle oopif detach', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
const frame = page.frames()[1];
expect(await frame.evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
const [detachedFrame] = await Promise.all([
page.waitForEvent('framedetached'),
page.evaluate(() => document.querySelector('iframe').remove()),
]);
expect(detachedFrame).toBe(frame);
});
it('should handle remote -> local -> remote transitions', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
await Promise.all([
page.frames()[1].waitForNavigation(),
page.evaluate(() => goLocal()),
]);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.PREFIX + '/grid.html');
expect(await countOOPIFs(browser)).toBe(0);
await Promise.all([
page.frames()[1].waitForNavigation(),
page.evaluate(() => goRemote()),
]);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
expect(await countOOPIFs(browser)).toBe(1);
});
it.fail(true)('should get the proper viewport', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => screen.width)).toBe(1280);
expect(await oopif.evaluate(() => screen.height)).toBe(720);
expect(await oopif.evaluate(() => matchMedia('(device-width: 1280px)').matches)).toBe(true);
expect(await oopif.evaluate(() => matchMedia('(device-height: 720px)').matches)).toBe(true);
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(false);
await page.setViewportSize({width: 123, height: 456});
expect(await oopif.evaluate(() => screen.width)).toBe(123);
expect(await oopif.evaluate(() => screen.height)).toBe(456);
expect(await oopif.evaluate(() => matchMedia('(device-width: 123px)').matches)).toBe(true);
expect(await oopif.evaluate(() => matchMedia('(device-height: 456px)').matches)).toBe(true);
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(false);
});
it('should expose function', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
await page.exposeFunction('mul', (a, b) => a * b);
const result = await oopif.evaluate(async function() {
return await mul(9, 4);
});
expect(result).toBe(36);
});
it('should emulate media', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
await page.emulateMedia({ colorScheme: 'dark' });
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
});
it('should emulate offline', async({sppBrowser, sppPage, sppContext, server}) => {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => navigator.onLine)).toBe(true);
await context.setOffline(true);
expect(await oopif.evaluate(() => navigator.onLine)).toBe(false);
});
it('should support context options', async({sppBrowser, server, playwright}) => {
const browser = sppBrowser;
const iPhone = playwright.devices['iPhone 6']
const context = await browser.newContext({ ...iPhone, timezoneId: 'America/Jamaica', locale: 'fr-CH', userAgent: 'UA' });
const page = await context.newPage();
const [request] = await Promise.all([
server.waitForRequest('/grid.html'),
page.goto(server.PREFIX + '/dynamic-oopif.html'),
]);
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(true);
expect(await oopif.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (heure normale de lEst nord-américain)');
expect(await oopif.evaluate(() => navigator.language)).toBe('fr-CH');
expect(await oopif.evaluate(() => navigator.userAgent)).toBe('UA');
expect(request.headers['user-agent']).toBe('UA');
await context.close();
});
it('should respect route', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
let intercepted = false;
await page.route('**/digits/0.png', route => {
intercepted = true;
route.continue();
});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(intercepted).toBe(true);
});
it('should take screenshot', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(await page.screenshot()).toBeGolden('screenshot-oopif.png');
});
it('should load oopif iframes with subresources and request interception', async function({sppBrowser, sppPage, server, context}) {
const browser = sppBrowser;
const page = sppPage;
await page.route('**/*', route => route.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
});
it('should report main requests', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
const requestFrames = [];
page.on('request', r => requestFrames.push(r.frame()));
const finishedFrames = [];
page.on('requestfinished', r => finishedFrames.push(r.frame()));
await page.goto(server.PREFIX + '/empty.html');
const main = page.mainFrame();
await main.evaluate(url => {
const iframe = document.createElement('iframe');
iframe.src = url;
document.body.appendChild(iframe);
return new Promise(f => iframe.onload = f);
}, server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(page.frames().length).toBe(2);
const child = main.childFrames()[0];
await child.waitForLoadState('domcontentloaded');
await child.evaluate(url => {
const iframe = document.createElement('iframe');
iframe.src = url;
document.body.appendChild(iframe);
return new Promise(f => iframe.onload = f);
}, server.PREFIX + '/empty.html');
expect(page.frames().length).toBe(3);
const grandChild = child.childFrames()[0];
await grandChild.waitForLoadState('domcontentloaded');
expect(await countOOPIFs(browser)).toBe(2);
expect(requestFrames[0]).toBe(main);
expect(finishedFrames[0]).toBe(main);
expect(requestFrames[1]).toBe(child);
expect(finishedFrames[1]).toBe(child);
expect(requestFrames[2]).toBe(grandChild);
expect(finishedFrames[2]).toBe(grandChild);
});
it('should support exposeFunction', async function({sppBrowser, sppContext, sppPage, server}) {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
await context.exposeFunction('dec', a => a - 1);
await page.exposeFunction('inc', a => a + 1);
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[0].evaluate(() => inc(3))).toBe(4);
expect(await page.frames()[1].evaluate(() => inc(4))).toBe(5);
expect(await page.frames()[0].evaluate(() => dec(3))).toBe(2);
expect(await page.frames()[1].evaluate(() => dec(4))).toBe(3);
});
it('should support addInitScript', async function({sppBrowser, sppContext, sppPage, server}) {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
await context.addInitScript(() => window.bar = 17);
await page.addInitScript(() => window.foo = 42);
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[0].evaluate(() => window.foo)).toBe(42);
expect(await page.frames()[1].evaluate(() => window.foo)).toBe(42);
expect(await page.frames()[0].evaluate(() => window.bar)).toBe(17);
expect(await page.frames()[1].evaluate(() => window.bar)).toBe(17);
});
// @see https://github.com/microsoft/playwright/issues/1240
it('should click a button when it overlays oopif', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/button-overlay-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
await page.click('button');
expect(await page.evaluate(() => window.BUTTON_CLICKED)).toBe(true);
});
it('should report google.com frame with headful', async({browserType, defaultBrowserOptions, server}) => {
// @see https://github.com/GoogleChrome/puppeteer/issues/2548
// https://google.com is isolated by default in Chromium embedder.
const browser = await browserType.launch({...defaultBrowserOptions, headless: false});
const page = await browser.newPage();
await page.goto(server.EMPTY_PAGE);
await page.route('**/*', route => {
route.fulfill({body: 'YO, GOOGLE.COM'});
});
await page.evaluate(() => {
const frame = document.createElement('iframe');
frame.setAttribute('src', 'https://google.com/');
document.body.appendChild(frame);
return new Promise(x => frame.onload = x);
});
await page.waitForSelector('iframe[src="https://google.com/"]');
expect(await countOOPIFs(browser)).toBe(1);
const urls = page.frames().map(frame => frame.url());
expect(urls).toEqual([
server.EMPTY_PAGE,
'https://google.com/'
]);
await browser.close();
});
it('ElementHandle.boundingBox() should work', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => {
iframe.style.width = '500px';
iframe.style.height = '500px';
iframe.style.marginLeft = '42px';
iframe.style.marginTop = '17px';
});
await page.frames()[1].goto(page.frames()[1].url());
expect(await countOOPIFs(browser)).toBe(1);
const handle1 = await page.frames()[1].$('.box:nth-of-type(13)');
expect(await handle1.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 });
await Promise.all([
page.frames()[1].waitForNavigation(),
page.evaluate(() => goLocal()),
]);
expect(await countOOPIFs(browser)).toBe(0);
const handle2 = await page.frames()[1].$('.box:nth-of-type(13)');
expect(await handle2.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 });
});
it('should click', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => {
iframe.style.width = '500px';
iframe.style.height = '500px';
iframe.style.marginLeft = '102px';
iframe.style.marginTop = '117px';
});
await page.frames()[1].goto(page.frames()[1].url());
expect(await countOOPIFs(browser)).toBe(1);
const handle1 = await page.frames()[1].$('.box:nth-of-type(13)');
await handle1.evaluate(div => div.addEventListener('click', () => window._clicked = true, false));
await handle1.click();
expect(await handle1.evaluate(() => window._clicked)).toBe(true);
});
});
async function countOOPIFs(browser) {
const browserSession = await browser.newBrowserCDPSession();
const oopifs = [];
browserSession.on('Target.targetCreated', async ({targetInfo}) => {
if (targetInfo.type === 'iframe')
oopifs.push(targetInfo);
});
await browserSession.send('Target.setDiscoverTargets', { discover: true });
await browserSession.detach();
return oopifs.length;
}

364
test/chromium/oopif.spec.js Normal file
View File

@ -0,0 +1,364 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
registerFixture('sppBrowser', async ({browserType, defaultBrowserOptions}, test) => {
const browser = await browserType.launch({
...defaultBrowserOptions,
args: (defaultBrowserOptions.args || []).concat(['--site-per-process'])
});
try {
await test(browser);
} finally {
await browser.close();
}
});
registerFixture('sppContext', async ({sppBrowser}, test) => {
const context = await sppBrowser.newContext();
try {
await test(context);
} finally {
await context.close();
}
});
registerFixture('sppPage', async ({sppContext}, test) => {
const page = await sppContext.newPage();
await test(page);
});
it.skip(!CHROMIUM)('should report oopif frames', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
});
it.skip(!CHROMIUM)('should handle oopif detach', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
const frame = page.frames()[1];
expect(await frame.evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
const [detachedFrame] = await Promise.all([
page.waitForEvent('framedetached'),
page.evaluate(() => document.querySelector('iframe').remove()),
]);
expect(detachedFrame).toBe(frame);
});
it.skip(!CHROMIUM)('should handle remote -> local -> remote transitions', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
await Promise.all([
page.frames()[1].waitForNavigation(),
page.evaluate(() => goLocal()),
]);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.PREFIX + '/grid.html');
expect(await countOOPIFs(browser)).toBe(0);
await Promise.all([
page.frames()[1].waitForNavigation(),
page.evaluate(() => goRemote()),
]);
expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
expect(await countOOPIFs(browser)).toBe(1);
});
it.fail(true)('should get the proper viewport', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
expect(page.viewportSize()).toEqual({width: 1280, height: 720});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => screen.width)).toBe(1280);
expect(await oopif.evaluate(() => screen.height)).toBe(720);
expect(await oopif.evaluate(() => matchMedia('(device-width: 1280px)').matches)).toBe(true);
expect(await oopif.evaluate(() => matchMedia('(device-height: 720px)').matches)).toBe(true);
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(false);
await page.setViewportSize({width: 123, height: 456});
expect(await oopif.evaluate(() => screen.width)).toBe(123);
expect(await oopif.evaluate(() => screen.height)).toBe(456);
expect(await oopif.evaluate(() => matchMedia('(device-width: 123px)').matches)).toBe(true);
expect(await oopif.evaluate(() => matchMedia('(device-height: 456px)').matches)).toBe(true);
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(false);
});
it.skip(!CHROMIUM)('should expose function', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
await page.exposeFunction('mul', (a, b) => a * b);
const result = await oopif.evaluate(async function() {
return await mul(9, 4);
});
expect(result).toBe(36);
});
it.skip(!CHROMIUM)('should emulate media', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
await page.emulateMedia({ colorScheme: 'dark' });
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
});
it.skip(!CHROMIUM)('should emulate offline', async({sppBrowser, sppPage, sppContext, server}) => {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => navigator.onLine)).toBe(true);
await context.setOffline(true);
expect(await oopif.evaluate(() => navigator.onLine)).toBe(false);
});
it.skip(!CHROMIUM)('should support context options', async({sppBrowser, server, playwright}) => {
const browser = sppBrowser;
const iPhone = playwright.devices['iPhone 6']
const context = await browser.newContext({ ...iPhone, timezoneId: 'America/Jamaica', locale: 'fr-CH', userAgent: 'UA' });
const page = await context.newPage();
const [request] = await Promise.all([
server.waitForRequest('/grid.html'),
page.goto(server.PREFIX + '/dynamic-oopif.html'),
]);
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
const oopif = page.frames()[1];
expect(await oopif.evaluate(() => 'ontouchstart' in window)).toBe(true);
expect(await oopif.evaluate(() => new Date(1479579154987).toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (heure normale de lEst nord-américain)');
expect(await oopif.evaluate(() => navigator.language)).toBe('fr-CH');
expect(await oopif.evaluate(() => navigator.userAgent)).toBe('UA');
expect(request.headers['user-agent']).toBe('UA');
await context.close();
});
it.skip(!CHROMIUM)('should respect route', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
let intercepted = false;
await page.route('**/digits/0.png', route => {
intercepted = true;
route.continue();
});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(intercepted).toBe(true);
});
it.skip(!CHROMIUM)('should take screenshot', async({sppBrowser, sppPage, server}) => {
const browser = sppBrowser;
const page = sppPage;
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
expect(await page.screenshot()).toBeGolden('screenshot-oopif.png');
});
it.skip(!CHROMIUM)('should load oopif iframes with subresources and request interception', async function({sppBrowser, sppPage, server, context}) {
const browser = sppBrowser;
const page = sppPage;
await page.route('**/*', route => route.continue());
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
});
it.skip(!CHROMIUM)('should report main requests', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
const requestFrames = [];
page.on('request', r => requestFrames.push(r.frame()));
const finishedFrames = [];
page.on('requestfinished', r => finishedFrames.push(r.frame()));
await page.goto(server.PREFIX + '/empty.html');
const main = page.mainFrame();
await main.evaluate(url => {
const iframe = document.createElement('iframe');
iframe.src = url;
document.body.appendChild(iframe);
return new Promise(f => iframe.onload = f);
}, server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(page.frames().length).toBe(2);
const child = main.childFrames()[0];
await child.waitForLoadState('domcontentloaded');
await child.evaluate(url => {
const iframe = document.createElement('iframe');
iframe.src = url;
document.body.appendChild(iframe);
return new Promise(f => iframe.onload = f);
}, server.PREFIX + '/empty.html');
expect(page.frames().length).toBe(3);
const grandChild = child.childFrames()[0];
await grandChild.waitForLoadState('domcontentloaded');
expect(await countOOPIFs(browser)).toBe(2);
expect(requestFrames[0]).toBe(main);
expect(finishedFrames[0]).toBe(main);
expect(requestFrames[1]).toBe(child);
expect(finishedFrames[1]).toBe(child);
expect(requestFrames[2]).toBe(grandChild);
expect(finishedFrames[2]).toBe(grandChild);
});
it.skip(!CHROMIUM)('should support exposeFunction', async function({sppBrowser, sppContext, sppPage, server}) {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
await context.exposeFunction('dec', a => a - 1);
await page.exposeFunction('inc', a => a + 1);
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[0].evaluate(() => inc(3))).toBe(4);
expect(await page.frames()[1].evaluate(() => inc(4))).toBe(5);
expect(await page.frames()[0].evaluate(() => dec(3))).toBe(2);
expect(await page.frames()[1].evaluate(() => dec(4))).toBe(3);
});
it.skip(!CHROMIUM)('should support addInitScript', async function({sppBrowser, sppContext, sppPage, server}) {
const browser = sppBrowser;
const context = sppContext;
const page = sppPage;
await context.addInitScript(() => window.bar = 17);
await page.addInitScript(() => window.foo = 42);
await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
expect(page.frames().length).toBe(2);
expect(await page.frames()[0].evaluate(() => window.foo)).toBe(42);
expect(await page.frames()[1].evaluate(() => window.foo)).toBe(42);
expect(await page.frames()[0].evaluate(() => window.bar)).toBe(17);
expect(await page.frames()[1].evaluate(() => window.bar)).toBe(17);
});
// @see https://github.com/microsoft/playwright/issues/1240
it.skip(!CHROMIUM)('should click a button when it overlays oopif', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/button-overlay-oopif.html');
expect(await countOOPIFs(browser)).toBe(1);
await page.click('button');
expect(await page.evaluate(() => window.BUTTON_CLICKED)).toBe(true);
});
it.skip(!CHROMIUM)('should report google.com frame with headful', async({browserType, defaultBrowserOptions, server}) => {
// @see https://github.com/GoogleChrome/puppeteer/issues/2548
// https://google.com is isolated by default in Chromium embedder.
const browser = await browserType.launch({...defaultBrowserOptions, headless: false});
const page = await browser.newPage();
await page.goto(server.EMPTY_PAGE);
await page.route('**/*', route => {
route.fulfill({body: 'YO, GOOGLE.COM'});
});
await page.evaluate(() => {
const frame = document.createElement('iframe');
frame.setAttribute('src', 'https://google.com/');
document.body.appendChild(frame);
return new Promise(x => frame.onload = x);
});
await page.waitForSelector('iframe[src="https://google.com/"]');
expect(await countOOPIFs(browser)).toBe(1);
const urls = page.frames().map(frame => frame.url());
expect(urls).toEqual([
server.EMPTY_PAGE,
'https://google.com/'
]);
await browser.close();
});
it.skip(!CHROMIUM)('ElementHandle.boundingBox() should work', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => {
iframe.style.width = '500px';
iframe.style.height = '500px';
iframe.style.marginLeft = '42px';
iframe.style.marginTop = '17px';
});
await page.frames()[1].goto(page.frames()[1].url());
expect(await countOOPIFs(browser)).toBe(1);
const handle1 = await page.frames()[1].$('.box:nth-of-type(13)');
expect(await handle1.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 });
await Promise.all([
page.frames()[1].waitForNavigation(),
page.evaluate(() => goLocal()),
]);
expect(await countOOPIFs(browser)).toBe(0);
const handle2 = await page.frames()[1].$('.box:nth-of-type(13)');
expect(await handle2.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 });
});
it.skip(!CHROMIUM)('should click', async function({sppBrowser, sppPage, server}) {
const browser = sppBrowser;
const page = sppPage;
await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => {
iframe.style.width = '500px';
iframe.style.height = '500px';
iframe.style.marginLeft = '102px';
iframe.style.marginTop = '117px';
});
await page.frames()[1].goto(page.frames()[1].url());
expect(await countOOPIFs(browser)).toBe(1);
const handle1 = await page.frames()[1].$('.box:nth-of-type(13)');
await handle1.evaluate(div => div.addEventListener('click', () => window._clicked = true, false));
await handle1.click();
expect(await handle1.evaluate(() => window._clicked)).toBe(true);
});
async function countOOPIFs(browser) {
const browserSession = await browser.newBrowserCDPSession();
const oopifs = [];
browserSession.on('Target.targetCreated', async ({targetInfo}) => {
if (targetInfo.type === 'iframe')
oopifs.push(targetInfo);
});
await browserSession.send('Target.setDiscoverTargets', { discover: true });
await browserSession.detach();
return oopifs.length;
}

View File

@ -1,112 +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, CHANNEL, USES_HOOKS} = testOptions;
describe.skip(!CHROMIUM)('ChromiumBrowserContext.createSession', function() {
it('should work', async function({page}) {
const client = await page.context().newCDPSession(page);
await Promise.all([
client.send('Runtime.enable'),
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
]);
const foo = await page.evaluate(() => window.foo);
expect(foo).toBe('bar');
});
it('should send events', async function({page, server}) {
const client = await page.context().newCDPSession(page);
await client.send('Network.enable');
const events = [];
client.on('Network.requestWillBeSent', event => events.push(event));
await page.goto(server.EMPTY_PAGE);
expect(events.length).toBe(1);
});
it('should enable and disable domains independently', async function({page}) {
const client = await page.context().newCDPSession(page);
await client.send('Runtime.enable');
await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain.
await page.coverage.startJSCoverage();
await page.coverage.stopJSCoverage();
page.on('console', console.log);
// generate a script in page and wait for the event.
await Promise.all([
new Promise(f => client.on('Debugger.scriptParsed', event => {
if (event.url === 'foo.js')
f();
})),
page.evaluate('//# sourceURL=foo.js')
]);
});
it('should be able to detach session', async function({page}) {
const client = await page.context().newCDPSession(page);
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await client.detach();
let error = null;
try {
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
} catch (e) {
error = e;
}
expect(error.message).toContain(CHANNEL ? 'Target browser or context has been closed' : 'Session closed.');
});
it('should throw nice errors', async function({page}) {
const client = await page.context().newCDPSession(page);
const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist');
async function theSourceOfTheProblems() {
await client.send('ThisCommand.DoesNotExist');
}
});
it('should not break page.close()', async function({browser}) {
const context = await browser.newContext();
const page = await context.newPage();
const session = await page.context().newCDPSession(page);
await session.detach();
await page.close();
await context.close();
});
it('should detach when page closes', async function({browser}) {
const context = await browser.newContext();
const page = await context.newPage();
const session = await context.newCDPSession(page);
await page.close();
let error;
await session.detach().catch(e => error = e);
expect(error).toBeTruthy();
await context.close();
});
});
describe.skip(!CHROMIUM)('ChromiumBrowser.newBrowserCDPSession', function() {
it('should work', async function({browser}) {
const session = await browser.newBrowserCDPSession();
const version = await session.send('Browser.getVersion');
expect(version.userAgent).toBeTruthy();
let gotEvent = false;
session.on('Target.targetCreated', () => gotEvent = true);
await session.send('Target.setDiscoverTargets', { discover: true });
expect(gotEvent).toBe(true);
await session.detach();
});
});

View File

@ -0,0 +1,115 @@
/**
* 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, CHANNEL, USES_HOOKS} = testOptions;
it.skip(!CHROMIUM)('should work', async function({page}) {
const client = await page.context().newCDPSession(page);
await Promise.all([
client.send('Runtime.enable'),
client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' })
]);
const foo = await page.evaluate(() => window.foo);
expect(foo).toBe('bar');
});
it.skip(!CHROMIUM)('should send events', async function({page, server}) {
const client = await page.context().newCDPSession(page);
await client.send('Network.enable');
const events = [];
client.on('Network.requestWillBeSent', event => events.push(event));
await page.goto(server.EMPTY_PAGE);
expect(events.length).toBe(1);
});
it.skip(!CHROMIUM)('should enable and disable domains independently', async function({page}) {
const client = await page.context().newCDPSession(page);
await client.send('Runtime.enable');
await client.send('Debugger.enable');
// JS coverage enables and then disables Debugger domain.
await page.coverage.startJSCoverage();
await page.coverage.stopJSCoverage();
page.on('console', console.log);
// generate a script in page and wait for the event.
await Promise.all([
new Promise(f => client.on('Debugger.scriptParsed', event => {
if (event.url === 'foo.js')
f();
})),
page.evaluate('//# sourceURL=foo.js')
]);
});
it.skip(!CHROMIUM)('should be able to detach session', async function({page}) {
const client = await page.context().newCDPSession(page);
await client.send('Runtime.enable');
const evalResponse = await client.send('Runtime.evaluate', {expression: '1 + 2', returnByValue: true});
expect(evalResponse.result.value).toBe(3);
await client.detach();
let error = null;
try {
await client.send('Runtime.evaluate', {expression: '3 + 1', returnByValue: true});
} catch (e) {
error = e;
}
expect(error.message).toContain(CHANNEL ? 'Target browser or context has been closed' : 'Session closed.');
});
it.skip(!CHROMIUM)('should throw nice errors', async function({page}) {
const client = await page.context().newCDPSession(page);
const error = await theSourceOfTheProblems().catch(error => error);
expect(error.stack).toContain('theSourceOfTheProblems');
expect(error.message).toContain('ThisCommand.DoesNotExist');
async function theSourceOfTheProblems() {
await client.send('ThisCommand.DoesNotExist');
}
});
it.skip(!CHROMIUM)('should not break page.close()', async function({browser}) {
const context = await browser.newContext();
const page = await context.newPage();
const session = await page.context().newCDPSession(page);
await session.detach();
await page.close();
await context.close();
});
it.skip(!CHROMIUM)('should detach when page closes', async function({browser}) {
const context = await browser.newContext();
const page = await context.newPage();
const session = await context.newCDPSession(page);
await page.close();
let error;
await session.detach().catch(e => error = e);
expect(error).toBeTruthy();
await context.close();
});
it.skip(!CHROMIUM)('should work', async function({browser}) {
const session = await browser.newBrowserCDPSession();
const version = await session.send('Browser.getVersion');
expect(version.userAgent).toBeTruthy();
let gotEvent = false;
session.on('Target.targetCreated', () => gotEvent = true);
await session.send('Target.setDiscoverTargets', { discover: true });
expect(gotEvent).toBe(true);
await session.detach();
});

View File

@ -1,70 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR, CHANNEL} = testOptions;
registerFixture('outputFile', async ({parallelIndex}, test) => {
const outputFile = path.join(OUTPUT_DIR, `trace-${parallelIndex}.json`);
await test(outputFile);
if (fs.existsSync(outputFile))
fs.unlinkSync(outputFile);
});
describe.skip(!CHROMIUM)('Chromium.startTracing', function() {
it('should output a trace', async({browser, page, server, outputFile}) => {
await browser.startTracing(page, {screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
await browser.stopTracing();
expect(fs.existsSync(outputFile)).toBe(true);
});
it('should run with custom categories if provided', async({browser, page, outputFile}) => {
await browser.startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
await browser.stopTracing();
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires', 'Does not contain expected category');
});
it('should throw if tracing on two pages', async({browser, page, outputFile}) => {
await browser.startTracing(page, {path: outputFile});
const newPage = await browser.newPage();
let error = null;
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
await newPage.close();
expect(error).toBeTruthy();
await browser.stopTracing();
});
it('should return a buffer', async({browser, page, server, outputFile}) => {
await browser.startTracing(page, {screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
const trace = await browser.stopTracing();
const buf = fs.readFileSync(outputFile);
expect(trace.toString()).toEqual(buf.toString(), 'Tracing buffer mismatch');
});
it('should work without options', async({browser, page, server}) => {
await browser.startTracing(page);
await page.goto(server.PREFIX + '/grid.html');
const trace = await browser.stopTracing();
expect(trace).toBeTruthy();
});
it('should support a buffer without a path', async({browser, page, server}) => {
await browser.startTracing(page, {screenshots: true});
await page.goto(server.PREFIX + '/grid.html');
const trace = await browser.stopTracing();
expect(trace.toString()).toContain('screenshot', 'Does not contain screenshot');
});
});

View File

@ -0,0 +1,73 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const {FFOX, CHROMIUM, WEBKIT, OUTPUT_DIR, CHANNEL} = testOptions;
registerFixture('outputFile', async ({parallelIndex}, test) => {
const outputFile = path.join(OUTPUT_DIR, `trace-${parallelIndex}.json`);
await test(outputFile);
if (fs.existsSync(outputFile))
fs.unlinkSync(outputFile);
});
it.skip(!CHROMIUM)('should output a trace', async({browser, page, server, outputFile}) => {
await browser.startTracing(page, {screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
await browser.stopTracing();
expect(fs.existsSync(outputFile)).toBe(true);
});
it.skip(!CHROMIUM)('should run with custom categories if provided', async({browser, page, outputFile}) => {
await browser.startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
await browser.stopTracing();
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires', 'Does not contain expected category');
});
it.skip(!CHROMIUM)('should throw if tracing on two pages', async({browser, page, outputFile}) => {
await browser.startTracing(page, {path: outputFile});
const newPage = await browser.newPage();
let error = null;
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
await newPage.close();
expect(error).toBeTruthy();
await browser.stopTracing();
});
it.skip(!CHROMIUM)('should return a buffer', async({browser, page, server, outputFile}) => {
await browser.startTracing(page, {screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html');
const trace = await browser.stopTracing();
const buf = fs.readFileSync(outputFile);
expect(trace.toString()).toEqual(buf.toString(), 'Tracing buffer mismatch');
});
it.skip(!CHROMIUM)('should work without options', async({browser, page, server}) => {
await browser.startTracing(page);
await page.goto(server.PREFIX + '/grid.html');
const trace = await browser.stopTracing();
expect(trace).toBeTruthy();
});
it.skip(!CHROMIUM)('should support a buffer without a path', async({browser, page, server}) => {
await browser.startTracing(page, {screenshots: true});
await page.goto(server.PREFIX + '/grid.html');
const trace = await browser.stopTracing();
expect(trace.toString()).toContain('screenshot', 'Does not contain screenshot');
});

View File

@ -0,0 +1,136 @@
/**
* 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 electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
const { CHROMIUM } = testOptions;
registerFixture('application', async ({playwright}, test) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')],
});
try {
await test(application);
} finally {
await application.close();
}
});
it.skip(!CHROMIUM)('should fire close event', async ({ playwright }) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')],
});
const events = [];
application.on('close', () => events.push('application'));
application.context().on('close', () => events.push('context'));
await application.close();
expect(events.join('|')).toBe('context|application');
// Give it some time to fire more events - there should not be any.
await new Promise(f => setTimeout(f, 1000));
expect(events.join('|')).toBe('context|application');
});
it.skip(!CHROMIUM)('should script application', async ({ application }) => {
const appPath = await application.evaluate(async ({ app }) => app.getAppPath());
expect(appPath).toContain('electron');
});
it.skip(!CHROMIUM)('should create window', async ({ application }) => {
const [ page ] = await Promise.all([
application.waitForEvent('window'),
application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World 1</title>');
})
]);
await page.waitForLoadState('domcontentloaded');
expect(await page.title()).toBe('Hello World 1');
});
it.skip(!CHROMIUM)('should create window 2', async ({ application }) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<title>Hello World 2</title>');
expect(await page.title()).toBe('Hello World 2');
});
it.skip(!CHROMIUM)('should create multiple windows', async ({ application }) => {
const createPage = async ordinal => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await Promise.all([
page.waitForNavigation(),
page.browserWindow.evaluate((window, ordinal) => window.loadURL(`data:text/html,<title>Hello World ${ordinal}</title>`), ordinal)
]);
return page;
};
const page1 = await createPage(1);
const page2 = await createPage(2);
const page3 = await createPage(3);
await page1.close();
const page4 = await createPage(4);
const titles = [];
for (const window of application.windows())
titles.push(await window.title());
expect(titles).toEqual(['Hello World 2', 'Hello World 3', 'Hello World 4']);
});
it.skip(!CHROMIUM)('should route network', async ({ application }) => {
await application.context().route('**/empty.html', (route, request) => {
route.fulfill({
status: 200,
contentType: 'text/html',
body: '<title>Hello World</title>',
})
});
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('https://localhost:1000/empty.html');
expect(await page.title()).toBe('Hello World');
});
it.skip(!CHROMIUM)('should support init script', async ({ application }) => {
await application.context().addInitScript('window.magic = 42;')
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window.copy = magic</script>');
expect(await page.evaluate(() => copy)).toBe(42);
});
it.skip(!CHROMIUM)('should expose function', async ({ application }) => {
const result = new Promise(f => callback = f);
const t = Date.now();
await application.context().exposeFunction('add', (a, b) => a + b);
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window.result = add(20, 22);</script>');
expect(await page.evaluate(() => result)).toBe(42);
});
it.skip(!CHROMIUM)('should wait for first window', async ({ application }) => {
application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World!</title>');
});
const window = await application.firstWindow();
expect(await window.title()).toBe('Hello World!');
});
it.skip(!CHROMIUM)('should have a clipboard instance', async ({ application }) => {
const clipboardContentToWrite = 'Hello from Playwright';
await application.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
const clipboardContentRead = await application.evaluate(async ({clipboard}) => clipboard.readText());
await expect(clipboardContentRead).toEqual(clipboardContentToWrite);
});

View File

@ -0,0 +1,70 @@
/**
* 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 electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
const { CHROMIUM } = testOptions;
registerFixture('application', async ({playwright}, test) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')],
});
try {
await test(application);
} finally {
await application.close();
}
});
registerFixture('window', async ({application}, test) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
try {
await test(page);
} finally {
await page.close();
}
});
it.skip(!CHROMIUM)('should click the button', async({window, server}) => {
await window.goto(server.PREFIX + '/input/button.html');
await window.click('button');
expect(await window.evaluate(() => result)).toBe('Clicked');
});
it.skip(!CHROMIUM)('should check the box', async({window}) => {
await window.setContent(`<input id='checkbox' type='checkbox'></input>`);
await window.check('input');
expect(await window.evaluate(() => checkbox.checked)).toBe(true);
});
it.skip(!CHROMIUM)('should not check the checked box', async({window}) => {
await window.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
await window.check('input');
expect(await window.evaluate(() => checkbox.checked)).toBe(true);
});
it.skip(!CHROMIUM)('should type into a textarea', async({window, server}) => {
await window.evaluate(() => {
const textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.focus();
});
const text = 'Hello world. I am the text that was typed!';
await window.keyboard.type(text);
expect(await window.evaluate(() => document.querySelector('textarea').value)).toBe(text);
});

View File

@ -1,166 +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 electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
const { CHROMIUM } = testOptions;
registerFixture('application', async ({playwright}, test) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')],
});
try {
await test(application);
} finally {
await application.close();
}
});
registerFixture('window', async ({application}, test) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
try {
await test(page);
} finally {
await page.close();
}
});
describe.skip(!CHROMIUM)('Electron', function() {
it('should fire close event', async ({ playwright }) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')],
});
const events = [];
application.on('close', () => events.push('application'));
application.context().on('close', () => events.push('context'));
await application.close();
expect(events.join('|')).toBe('context|application');
// Give it some time to fire more events - there should not be any.
await new Promise(f => setTimeout(f, 1000));
expect(events.join('|')).toBe('context|application');
});
it('should script application', async ({ application }) => {
const appPath = await application.evaluate(async ({ app }) => app.getAppPath());
expect(appPath).toContain('electron');
});
it('should create window', async ({ application }) => {
const [ page ] = await Promise.all([
application.waitForEvent('window'),
application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World 1</title>');
})
]);
await page.waitForLoadState('domcontentloaded');
expect(await page.title()).toBe('Hello World 1');
});
it('should create window 2', async ({ application }) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<title>Hello World 2</title>');
expect(await page.title()).toBe('Hello World 2');
});
it('should create multiple windows', async ({ application }) => {
const createPage = async ordinal => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await Promise.all([
page.waitForNavigation(),
page.browserWindow.evaluate((window, ordinal) => window.loadURL(`data:text/html,<title>Hello World ${ordinal}</title>`), ordinal)
]);
return page;
};
const page1 = await createPage(1);
const page2 = await createPage(2);
const page3 = await createPage(3);
await page1.close();
const page4 = await createPage(4);
const titles = [];
for (const window of application.windows())
titles.push(await window.title());
expect(titles).toEqual(['Hello World 2', 'Hello World 3', 'Hello World 4']);
});
it('should route network', async ({ application }) => {
await application.context().route('**/empty.html', (route, request) => {
route.fulfill({
status: 200,
contentType: 'text/html',
body: '<title>Hello World</title>',
})
});
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('https://localhost:1000/empty.html');
expect(await page.title()).toBe('Hello World');
});
it('should support init script', async ({ application }) => {
await application.context().addInitScript('window.magic = 42;')
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window.copy = magic</script>');
expect(await page.evaluate(() => copy)).toBe(42);
});
it('should expose function', async ({ application }) => {
const result = new Promise(f => callback = f);
const t = Date.now();
await application.context().exposeFunction('add', (a, b) => a + b);
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window.result = add(20, 22);</script>');
expect(await page.evaluate(() => result)).toBe(42);
});
it('should wait for first window', async ({ application }) => {
application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World!</title>');
});
const window = await application.firstWindow();
expect(await window.title()).toBe('Hello World!');
});
it('should have a clipboard instance', async ({ application }) => {
const clipboardContentToWrite = 'Hello from Playwright';
await application.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
const clipboardContentRead = await application.evaluate(async ({clipboard}) => clipboard.readText());
await expect(clipboardContentRead).toEqual(clipboardContentToWrite);
});
});
describe.skip(!CHROMIUM)('Electron per window', function() {
it('should click the button', async({window, server}) => {
await window.goto(server.PREFIX + '/input/button.html');
await window.click('button');
expect(await window.evaluate(() => result)).toBe('Clicked');
});
it('should check the box', async({window}) => {
await window.setContent(`<input id='checkbox' type='checkbox'></input>`);
await window.check('input');
expect(await window.evaluate(() => checkbox.checked)).toBe(true);
});
it('should not check the checked box', async({window}) => {
await window.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
await window.check('input');
expect(await window.evaluate(() => checkbox.checked)).toBe(true);
});
it('should type into a textarea', async({window, server}) => {
await window.evaluate(() => {
const textarea = document.createElement('textarea');
document.body.appendChild(textarea);
textarea.focus();
});
const text = 'Hello world. I am the text that was typed!';
await window.keyboard.type(text);
expect(await window.evaluate(() => document.querySelector('textarea').value)).toBe(text);
});
});

View File

@ -16,19 +16,17 @@
const { FFOX } = testOptions;
describe.skip(!FFOX)('launcher', function() {
it('should pass firefox user preferences', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch({
...defaultBrowserOptions,
firefoxUserPrefs: {
'network.proxy.type': 1,
'network.proxy.http': '127.0.0.1',
'network.proxy.http_port': 3333,
}
});
const page = await browser.newPage();
const error = await page.goto('http://example.com').catch(e => e);
expect(error.message).toContain('NS_ERROR_PROXY_CONNECTION_REFUSED');
await browser.close();
it.skip(!FFOX)('should pass firefox user preferences', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch({
...defaultBrowserOptions,
firefoxUserPrefs: {
'network.proxy.type': 1,
'network.proxy.http': '127.0.0.1',
'network.proxy.http_port': 3333,
}
});
const page = await browser.newPage();
const error = await page.goto('http://example.com').catch(e => e);
expect(error.message).toContain('NS_ERROR_PROXY_CONNECTION_REFUSED');
await browser.close();
});

View File

@ -1,192 +0,0 @@
/**
* Copyright 2019 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 path = require('path');
const {spawn, execSync} = require('child_process');
const {FFOX, CHROMIUM, WEBKIT, WIN, LINUX, HEADLESS} = testOptions;
const playwrightPath = path.join(__dirname, '..');
class Wrapper {
constructor(browserType, defaultBrowserOptions, extraOptions) {
this._output = new Map();
this._outputCallback = new Map();
this._browserType = browserType;
const launchOptions = {...defaultBrowserOptions,
handleSIGINT: true,
handleSIGTERM: true,
handleSIGHUP: true,
executablePath: browserType.executablePath(),
logger: undefined,
};
const options = {
playwrightPath,
browserTypeName: browserType.name(),
launchOptions,
...extraOptions,
};
this._child = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), JSON.stringify(options)]);
this._child.on('error', (...args) => console.log("ERROR", ...args));
this._exitPromise = new Promise(resolve => this._child.on('exit', resolve));
let outputString = '';
this._child.stdout.on('data', data => {
outputString += data.toString();
// Uncomment to debug.
// console.log(data.toString());
let match;
while (match = outputString.match(/\(([^()]+)=>([^()]+)\)/)) {
const key = match[1];
const value = match[2];
this._addOutput(key, value);
outputString = outputString.substring(match.index + match[0].length);
}
});
}
_addOutput(key, value) {
this._output.set(key, value);
const cb = this._outputCallback.get(key);
this._outputCallback.delete(key);
if (cb)
cb();
}
async out(key) {
if (!this._output.has(key))
await new Promise(f => this._outputCallback.set(key, f));
return this._output.get(key);
}
async connect() {
const wsEndpoint = await this.out('wsEndpoint');
const browser = await this._browserType.connect({ wsEndpoint });
this._exitAndDisconnectPromise = Promise.all([
this._exitPromise,
new Promise(resolve => browser.once('disconnected', resolve)),
]).then(([exitCode]) => exitCode);
}
child() {
return this._child;
}
async childExitCode() {
return await this._exitAndDisconnectPromise;
}
}
registerFixture('wrapper', async ({browserType, defaultBrowserOptions}, test) => {
const wrapper = new Wrapper(browserType, defaultBrowserOptions);
await wrapper.connect();
await test(wrapper);
});
registerFixture('stallingWrapper', async ({browserType, defaultBrowserOptions}, test) => {
const wrapper = new Wrapper(browserType, defaultBrowserOptions, { stallOnClose: true });
await wrapper.connect();
await test(wrapper);
});
describe('Fixtures', function() {
it.slow()('should close the browser when the node process closes', async ({wrapper}) => {
if (WIN)
execSync(`taskkill /pid ${wrapper.child().pid} /T /F`);
else
process.kill(wrapper.child().pid);
expect(await wrapper.childExitCode()).toBe(WIN ? 1 : 0);
// We might not get browser exitCode in time when killing the parent node process,
// so we don't check it here.
});
describe.skip(WIN || !HEADLESS)('signals', () => {
// Cannot reliably send signals on Windows.
it.slow()('should report browser close signal', async ({wrapper}) => {
const pid = await wrapper.out('pid');
process.kill(-pid, 'SIGTERM');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGTERM');
process.kill(wrapper.child().pid);
await wrapper.childExitCode();
});
it.slow()('should report browser close signal 2', async ({wrapper}) => {
const pid = await wrapper.out('pid');
process.kill(-pid, 'SIGKILL');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
process.kill(wrapper.child().pid);
await wrapper.childExitCode();
});
it.slow()('should close the browser on SIGINT', async ({wrapper}) => {
process.kill(wrapper.child().pid, 'SIGINT');
expect(await wrapper.out('exitCode')).toBe('0');
expect(await wrapper.out('signal')).toBe('null');
expect(await wrapper.childExitCode()).toBe(130);
});
it.slow()('should close the browser on SIGTERM', async ({wrapper}) => {
process.kill(wrapper.child().pid, 'SIGTERM');
expect(await wrapper.out('exitCode')).toBe('0');
expect(await wrapper.out('signal')).toBe('null');
expect(await wrapper.childExitCode()).toBe(0);
});
it.slow()('should close the browser on SIGHUP', async ({wrapper}) => {
process.kill(wrapper.child().pid, 'SIGHUP');
expect(await wrapper.out('exitCode')).toBe('0');
expect(await wrapper.out('signal')).toBe('null');
expect(await wrapper.childExitCode()).toBe(0);
});
it.slow()('should kill the browser on double SIGINT', async ({stallingWrapper}) => {
const wrapper = stallingWrapper;
process.kill(wrapper.child().pid, 'SIGINT');
await wrapper.out('stalled');
process.kill(wrapper.child().pid, 'SIGINT');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
expect(await wrapper.childExitCode()).toBe(130);
});
it.slow()('should kill the browser on SIGINT + SIGTERM', async ({stallingWrapper}) => {
const wrapper = stallingWrapper;
process.kill(wrapper.child().pid, 'SIGINT');
await wrapper.out('stalled');
process.kill(wrapper.child().pid, 'SIGTERM');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
expect(await wrapper.childExitCode()).toBe(0);
});
it.slow()('should kill the browser on SIGTERM + SIGINT', async ({stallingWrapper}) => {
const wrapper = stallingWrapper;
process.kill(wrapper.child().pid, 'SIGTERM');
await wrapper.out('stalled');
process.kill(wrapper.child().pid, 'SIGINT');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
expect(await wrapper.childExitCode()).toBe(130);
});
});
});
describe('StackTrace', () => {
it('caller file path', async ({}) => {
const stackTrace = require(path.join(playwrightPath, 'lib', 'utils', 'stackTrace'));
const callme = require('./fixtures/callback');
const filePath = callme(() => {
return stackTrace.getCallerFilePath(path.join(__dirname, 'fixtures') + path.sep);
});
expect(filePath).toBe(__filename);
});
});

193
test/fixtures.spec.js Normal file
View File

@ -0,0 +1,193 @@
/**
* Copyright 2019 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 path = require('path');
const {spawn, execSync} = require('child_process');
const {FFOX, CHROMIUM, WEBKIT, WIN, LINUX, HEADLESS} = testOptions;
const playwrightPath = path.join(__dirname, '..');
class Wrapper {
constructor(browserType, defaultBrowserOptions, extraOptions) {
this._output = new Map();
this._outputCallback = new Map();
this._browserType = browserType;
const launchOptions = {...defaultBrowserOptions,
handleSIGINT: true,
handleSIGTERM: true,
handleSIGHUP: true,
executablePath: browserType.executablePath(),
logger: undefined,
};
const options = {
playwrightPath,
browserTypeName: browserType.name(),
launchOptions,
...extraOptions,
};
this._child = spawn('node', [path.join(__dirname, 'fixtures', 'closeme.js'), JSON.stringify(options)]);
this._child.on('error', (...args) => console.log("ERROR", ...args));
this._exitPromise = new Promise(resolve => this._child.on('exit', resolve));
let outputString = '';
this._child.stdout.on('data', data => {
outputString += data.toString();
// Uncomment to debug.
// console.log(data.toString());
let match;
while (match = outputString.match(/\(([^()]+)=>([^()]+)\)/)) {
const key = match[1];
const value = match[2];
this._addOutput(key, value);
outputString = outputString.substring(match.index + match[0].length);
}
});
}
_addOutput(key, value) {
this._output.set(key, value);
const cb = this._outputCallback.get(key);
this._outputCallback.delete(key);
if (cb)
cb();
}
async out(key) {
if (!this._output.has(key))
await new Promise(f => this._outputCallback.set(key, f));
return this._output.get(key);
}
async connect() {
const wsEndpoint = await this.out('wsEndpoint');
const browser = await this._browserType.connect({ wsEndpoint });
this._exitAndDisconnectPromise = Promise.all([
this._exitPromise,
new Promise(resolve => browser.once('disconnected', resolve)),
]).then(([exitCode]) => exitCode);
}
child() {
return this._child;
}
async childExitCode() {
return await this._exitAndDisconnectPromise;
}
}
registerFixture('wrapper', async ({browserType, defaultBrowserOptions}, test) => {
const wrapper = new Wrapper(browserType, defaultBrowserOptions);
await wrapper.connect();
await test(wrapper);
});
registerFixture('stallingWrapper', async ({browserType, defaultBrowserOptions}, test) => {
const wrapper = new Wrapper(browserType, defaultBrowserOptions, { stallOnClose: true });
await wrapper.connect();
await test(wrapper);
});
it.slow()('should close the browser when the node process closes', async ({wrapper}) => {
if (WIN)
execSync(`taskkill /pid ${wrapper.child().pid} /T /F`);
else
process.kill(wrapper.child().pid);
expect(await wrapper.childExitCode()).toBe(WIN ? 1 : 0);
// We might not get browser exitCode in time when killing the parent node process,
// so we don't check it here.
});
// Cannot reliably send signals on Windows.
it.skip(WIN || !HEADLESS).slow()('should report browser close signal', async ({wrapper}) => {
const pid = await wrapper.out('pid');
process.kill(-pid, 'SIGTERM');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGTERM');
process.kill(wrapper.child().pid);
await wrapper.childExitCode();
});
it.skip(WIN || !HEADLESS).slow()('should report browser close signal 2', async ({wrapper}) => {
const pid = await wrapper.out('pid');
process.kill(-pid, 'SIGKILL');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
process.kill(wrapper.child().pid);
await wrapper.childExitCode();
});
it.skip(WIN || !HEADLESS).slow()('should close the browser on SIGINT', async ({wrapper}) => {
process.kill(wrapper.child().pid, 'SIGINT');
expect(await wrapper.out('exitCode')).toBe('0');
expect(await wrapper.out('signal')).toBe('null');
expect(await wrapper.childExitCode()).toBe(130);
});
it.skip(WIN || !HEADLESS).slow()('should close the browser on SIGTERM', async ({wrapper}) => {
process.kill(wrapper.child().pid, 'SIGTERM');
expect(await wrapper.out('exitCode')).toBe('0');
expect(await wrapper.out('signal')).toBe('null');
expect(await wrapper.childExitCode()).toBe(0);
});
it.skip(WIN || !HEADLESS).slow()('should close the browser on SIGHUP', async ({wrapper}) => {
process.kill(wrapper.child().pid, 'SIGHUP');
expect(await wrapper.out('exitCode')).toBe('0');
expect(await wrapper.out('signal')).toBe('null');
expect(await wrapper.childExitCode()).toBe(0);
});
it.skip(WIN || !HEADLESS).slow()('should kill the browser on double SIGINT', async ({stallingWrapper}) => {
const wrapper = stallingWrapper;
process.kill(wrapper.child().pid, 'SIGINT');
await wrapper.out('stalled');
process.kill(wrapper.child().pid, 'SIGINT');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
expect(await wrapper.childExitCode()).toBe(130);
});
it.skip(WIN || !HEADLESS).slow()('should kill the browser on SIGINT + SIGTERM', async ({stallingWrapper}) => {
const wrapper = stallingWrapper;
process.kill(wrapper.child().pid, 'SIGINT');
await wrapper.out('stalled');
process.kill(wrapper.child().pid, 'SIGTERM');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
expect(await wrapper.childExitCode()).toBe(0);
});
it.skip(WIN || !HEADLESS).slow()('should kill the browser on SIGTERM + SIGINT', async ({stallingWrapper}) => {
const wrapper = stallingWrapper;
process.kill(wrapper.child().pid, 'SIGTERM');
await wrapper.out('stalled');
process.kill(wrapper.child().pid, 'SIGINT');
expect(await wrapper.out('exitCode')).toBe('null');
expect(await wrapper.out('signal')).toBe('SIGKILL');
expect(await wrapper.childExitCode()).toBe(130);
});
it('caller file path', async ({}) => {
const stackTrace = require(path.join(playwrightPath, 'lib', 'utils', 'stackTrace'));
const callme = require('./fixtures/callback');
const filePath = callme(() => {
return stackTrace.getCallerFilePath(path.join(__dirname, 'fixtures') + path.sep);
});
expect(filePath).toBe(__filename);
});

View File

@ -1,314 +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 path = require('path');
const fs = require('fs');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
describe('Playwright', function() {
describe('browserType.launch', function() {
it('should reject all promises when browser is closed', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
const page = await (await browser.newContext()).newPage();
let error = null;
const neverResolves = page.evaluate(() => new Promise(r => {})).catch(e => error = e);
await page.evaluate(() => new Promise(f => setTimeout(f, 0)));
await browser.close();
await neverResolves;
expect(error.message).toContain('Protocol error');
});
it('should throw if userDataDir option is passed', async({browserType, defaultBrowserOptions}) => {
let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, {userDataDir: 'random-path'});
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('launchPersistentContext');
});
it.skip(FFOX)('should throw if page argument is passed', async({browserType, defaultBrowserOptions}) => {
let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] });
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('can not specify page');
});
it.fail(true)('should reject if launched browser fails immediately', async({browserType, defaultBrowserOptions}) => {
// I'm getting ENCONRESET on this one.
const options = Object.assign({}, defaultBrowserOptions, {executablePath: path.join(__dirname, 'assets', 'dummy_bad_browser_executable.js')});
let waitError = null;
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('== logs ==');
});
it('should reject if executable path is invalid', async({browserType, defaultBrowserOptions}) => {
let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, {executablePath: 'random-invalid-path'});
await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('Failed to launch');
});
it.skip(USES_HOOKS)('should handle timeout', async({browserType, defaultBrowserOptions}) => {
const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain(`browserType.launch: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`[browser] <launching>`);
expect(error.message).toContain(`[browser] <launched> pid=`);
});
it.skip(USES_HOOKS)('should handle exception', async({browserType, defaultBrowserOptions}) => {
const e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 };
const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain('Dummy');
});
it.skip(USES_HOOKS)('should report launch log', async({browserType, defaultBrowserOptions}) => {
const e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 };
const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain('<launching>');
});
it.slow()('should accept objects as options', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch({ ...defaultBrowserOptions, process });
await browser.close();
});
});
describe('browserType.executablePath', function() {
it('should work', async({browserType}) => {
const executablePath = browserType.executablePath();
expect(fs.existsSync(executablePath)).toBe(true);
expect(fs.realpathSync(executablePath)).toBe(executablePath);
});
});
describe('browserType.name', function() {
it('should work', async({browserType}) => {
if (WEBKIT)
expect(browserType.name()).toBe('webkit');
else if (FFOX)
expect(browserType.name()).toBe('firefox');
else if (CHROMIUM)
expect(browserType.name()).toBe('chromium');
else
throw new Error('Unknown browser');
});
});
});
describe('Top-level requires', function() {
it('should require top-level Errors', async({playwright}) => {
const Errors = require(path.join(utils.projectRoot(), '/lib/errors.js'));
expect(String(Errors.TimeoutError)).toContain('TimeoutError');
});
it('should require top-level DeviceDescriptors', async({playwright}) => {
const Devices = require(path.join(utils.projectRoot(), '/lib/deviceDescriptors.js')).DeviceDescriptors;
expect(Devices['iPhone 6']).toBeTruthy();
expect(Devices['iPhone 6']).toEqual(playwright.devices['iPhone 6']);
});
});
describe('Browser.isConnected', () => {
it('should set the browser connected state', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
expect(remote.isConnected()).toBe(true);
await remote.close();
expect(remote.isConnected()).toBe(false);
await browserServer._checkLeaks();
await browserServer.close();
});
it('should throw when used after isConnected returns false', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
await Promise.all([
browserServer.close(),
new Promise(f => remote.once('disconnected', f)),
]);
expect(remote.isConnected()).toBe(false);
const error = await page.evaluate('1 + 1').catch(e => e);
expect(error.message).toContain('has been closed');
});
});
describe('Browser.disconnect', function() {
it('should reject navigation when browser closes', async({browserType, defaultBrowserOptions, server}) => {
server.setRoute('/one-style.css', () => {});
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
const navigationPromise = page.goto(server.PREFIX + '/one-style.html', {timeout: 60000}).catch(e => e);
await server.waitForRequest('/one-style.css');
await remote.close();
const error = await navigationPromise;
expect(error.message).toContain('Navigation failed because page was closed!');
await browserServer._checkLeaks();
await browserServer.close();
});
it('should reject waitForSelector when browser closes', async({browserType, defaultBrowserOptions, server}) => {
server.setRoute('/empty.html', () => {});
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
const watchdog = page.waitForSelector('div', { state: 'attached', timeout: 60000 }).catch(e => e);
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
await page.waitForSelector('body', { state: 'attached' });
await remote.close();
const error = await watchdog;
expect(error.message).toContain('Protocol error');
await browserServer._checkLeaks();
await browserServer.close();
});
it('should throw if used after disconnect', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page = await remote.newPage();
await remote.close();
const error = await page.evaluate('1 + 1').catch(e => e);
expect(error.message).toContain('has been closed');
await browserServer._checkLeaks();
await browserServer.close();
});
it('should emit close events on pages and contexts', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const context = await remote.newContext();
const page = await context.newPage();
let pageClosed = false;
page.on('close', e => pageClosed = true);
await Promise.all([
new Promise(f => context.on('close', f)),
browserServer.close()
]);
expect(pageClosed).toBeTruthy();
});
});
describe('Browser.close', function() {
it('should terminate network waiters', async({browserType, defaultBrowserOptions, server}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const newPage = await remote.newPage();
const results = await Promise.all([
newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e),
newPage.waitForResponse(server.EMPTY_PAGE).catch(e => e),
browserServer.close()
]);
for (let i = 0; i < 2; i++) {
const message = results[i].message;
expect(message).toContain('Page closed');
expect(message).not.toContain('Timeout');
}
});
it('should fire close event for all contexts', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
const context = await browser.newContext();
let closed = false;
context.on('close', () => closed = true);
await browser.close();
expect(closed).toBe(true);
});
it('should be callable twice', async({browserType, defaultBrowserOptions}) => {
const browser = await browserType.launch(defaultBrowserOptions);
await Promise.all([
browser.close(),
browser.close(),
]);
await browser.close();
});
});
describe('browserType.launchServer', function() {
it('should work', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browserContext = await browser.newContext();
expect(browserContext.pages().length).toBe(0);
expect(browserServer.wsEndpoint()).not.toBe(null);
const page = await browserContext.newPage();
expect(await page.evaluate('11 * 11')).toBe(121);
await page.close();
await browser.close();
await browserServer._checkLeaks();
await browserServer.close();
});
it('should fire "disconnected" when closing the server', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
const closedPromise = new Promise(f => browserServer.on('close', f));
browserServer.kill();
await Promise.all([
disconnectedEventPromise,
closedPromise,
]);
});
it('should fire "close" event during kill', async({browserType, defaultBrowserOptions}) => {
const order = [];
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const closedPromise = new Promise(f => browserServer.on('close', () => {
order.push('closed');
f();
}));
await Promise.all([
browserServer.kill().then(() => order.push('killed')),
closedPromise,
]);
expect(order).toEqual(['closed', 'killed']);
});
it('should return child_process instance', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
expect(browserServer.process().pid).toBeGreaterThan(0);
await browserServer.close();
});
it('should fire close event', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const [result] = await Promise.all([
new Promise(f => browserServer.on('close', (exitCode, signal) => f({ exitCode, signal }))),
browserServer.close(),
]);
expect(result.exitCode).toBe(0);
expect(result.signal).toBe(null);
});
});
describe('browserType.connect', function() {
it.slow()('should be able to reconnect to a browser', async({browserType, defaultBrowserOptions, server}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
{
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browserContext = await browser.newContext();
const page = await browserContext.newPage();
await page.goto(server.EMPTY_PAGE);
await browser.close();
}
{
const browser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browserContext = await browser.newContext();
const page = await browserContext.newPage();
await page.goto(server.EMPTY_PAGE);
await browser.close();
}
await browserServer._checkLeaks();
await browserServer.close();
});
it.fail(USES_HOOKS || (CHROMIUM && WIN)).slow()('should handle exceptions during connect', async({browserType, defaultBrowserOptions, server}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const __testHookBeforeCreateBrowser = () => { throw new Error('Dummy') };
const error = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint(), __testHookBeforeCreateBrowser }).catch(e => e);
await browserServer._checkLeaks();
await browserServer.close();
expect(error.message).toContain('Dummy');
});
});

32
test/launcher.spec.js Normal file
View File

@ -0,0 +1,32 @@
/**
* 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 path = require('path');
const fs = require('fs');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, WIN, USES_HOOKS, CHANNEL} = testOptions;
it('should require top-level Errors', async({playwright}) => {
const Errors = require(path.join(utils.projectRoot(), '/lib/errors.js'));
expect(String(Errors.TimeoutError)).toContain('TimeoutError');
});
it('should require top-level DeviceDescriptors', async({playwright}) => {
const Devices = require(path.join(utils.projectRoot(), '/lib/deviceDescriptors.js')).DeviceDescriptors;
expect(Devices['iPhone 6']).toBeTruthy();
expect(Devices['iPhone 6']).toEqual(playwright.devices['iPhone 6']);
});

View File

@ -98,7 +98,7 @@ it('should trigger correct Log', async({page, server}) => {
expect(message.type()).toEqual('error');
});
it.only('should have location for console API calls', async({page, server}) => {
it('should have location for console API calls', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const [message] = await Promise.all([
page.waitForEvent('console', m => m.text() === 'yellow' ),