From 4cbfa09c2cb910c13b59b6d9b5544bacc823329a Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Tue, 4 Aug 2020 15:57:25 -0700 Subject: [PATCH] test: remove describes (5) (#3294) --- test/browsertype-basic.spec.js | 38 ++ test/browsertype-connect.spec.js | 73 ++++ test/browsertype-launch-server.spec.js | 149 +++++++ test/browsertype-launch.spec.js | 106 +++++ test/chromium/chromium.jest.js | 88 ----- test/chromium/chromium.spec.js | 87 +++++ test/chromium/launcher.jest.js | 90 ----- test/chromium/launcher.spec.js | 86 +++++ test/chromium/oopif.jest.js | 350 ----------------- test/chromium/oopif.spec.js | 364 ++++++++++++++++++ test/chromium/session.jest.js | 112 ------ test/chromium/session.spec.js | 115 ++++++ test/chromium/tracing.jest.js | 70 ---- test/chromium/tracing.spec.js | 73 ++++ test/electron/electron-app.spec.js | 136 +++++++ test/electron/electron-window.spec.js | 70 ++++ test/electron/electron.jest.js | 166 -------- .../{launcher.jest.js => launcher.spec.js} | 26 +- test/fixtures.jest.js | 192 --------- test/fixtures.spec.js | 193 ++++++++++ test/launcher.jest.js | 314 --------------- test/launcher.spec.js | 32 ++ test/page-event-console.spec.js | 2 +- 23 files changed, 1535 insertions(+), 1397 deletions(-) create mode 100644 test/browsertype-basic.spec.js create mode 100644 test/browsertype-connect.spec.js create mode 100644 test/browsertype-launch-server.spec.js create mode 100644 test/browsertype-launch.spec.js delete mode 100644 test/chromium/chromium.jest.js create mode 100644 test/chromium/chromium.spec.js delete mode 100644 test/chromium/launcher.jest.js create mode 100644 test/chromium/launcher.spec.js delete mode 100644 test/chromium/oopif.jest.js create mode 100644 test/chromium/oopif.spec.js delete mode 100644 test/chromium/session.jest.js create mode 100644 test/chromium/session.spec.js delete mode 100644 test/chromium/tracing.jest.js create mode 100644 test/chromium/tracing.spec.js create mode 100644 test/electron/electron-app.spec.js create mode 100644 test/electron/electron-window.spec.js delete mode 100644 test/electron/electron.jest.js rename test/firefox/{launcher.jest.js => launcher.spec.js} (52%) delete mode 100644 test/fixtures.jest.js create mode 100644 test/fixtures.spec.js delete mode 100644 test/launcher.jest.js create mode 100644 test/launcher.spec.js diff --git a/test/browsertype-basic.spec.js b/test/browsertype-basic.spec.js new file mode 100644 index 0000000000..1207710f47 --- /dev/null +++ b/test/browsertype-basic.spec.js @@ -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'); +}); diff --git a/test/browsertype-connect.spec.js b/test/browsertype-connect.spec.js new file mode 100644 index 0000000000..28f3968f64 --- /dev/null +++ b/test/browsertype-connect.spec.js @@ -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'); +}); diff --git a/test/browsertype-launch-server.spec.js b/test/browsertype-launch-server.spec.js new file mode 100644 index 0000000000..cc30fb15ab --- /dev/null +++ b/test/browsertype-launch-server.spec.js @@ -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'); + } +}); diff --git a/test/browsertype-launch.spec.js b/test/browsertype-launch.spec.js new file mode 100644 index 0000000000..98f93bec8e --- /dev/null +++ b/test/browsertype-launch.spec.js @@ -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] `); + expect(error.message).toContain(`[browser] 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(''); +}); + +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(); +}); diff --git a/test/chromium/chromium.jest.js b/test/chromium/chromium.jest.js deleted file mode 100644 index 7daf351ffb..0000000000 --- a/test/chromium/chromium.jest.js +++ /dev/null @@ -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(` - - `)); - 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'); - }); -}); diff --git a/test/chromium/chromium.spec.js b/test/chromium/chromium.spec.js new file mode 100644 index 0000000000..7aa6998ed9 --- /dev/null +++ b/test/chromium/chromium.spec.js @@ -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(` + + `)); + 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'); +}); diff --git a/test/chromium/launcher.jest.js b/test/chromium/launcher.jest.js deleted file mode 100644 index c47720790d..0000000000 --- a/test/chromium/launcher.jest.js +++ /dev/null @@ -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); - }); -}); diff --git a/test/chromium/launcher.spec.js b/test/chromium/launcher.spec.js new file mode 100644 index 0000000000..19f64cb8c3 --- /dev/null +++ b/test/chromium/launcher.spec.js @@ -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); +}); diff --git a/test/chromium/oopif.jest.js b/test/chromium/oopif.jest.js deleted file mode 100644 index 539644f890..0000000000 --- a/test/chromium/oopif.jest.js +++ /dev/null @@ -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 l’Est 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; -} diff --git a/test/chromium/oopif.spec.js b/test/chromium/oopif.spec.js new file mode 100644 index 0000000000..547cdab37e --- /dev/null +++ b/test/chromium/oopif.spec.js @@ -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 l’Est 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; +} diff --git a/test/chromium/session.jest.js b/test/chromium/session.jest.js deleted file mode 100644 index ae8107790d..0000000000 --- a/test/chromium/session.jest.js +++ /dev/null @@ -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(); - }); -}); diff --git a/test/chromium/session.spec.js b/test/chromium/session.spec.js new file mode 100644 index 0000000000..b31783f7bb --- /dev/null +++ b/test/chromium/session.spec.js @@ -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(); +}); diff --git a/test/chromium/tracing.jest.js b/test/chromium/tracing.jest.js deleted file mode 100644 index 3c9c2545cf..0000000000 --- a/test/chromium/tracing.jest.js +++ /dev/null @@ -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'); - }); -}); diff --git a/test/chromium/tracing.spec.js b/test/chromium/tracing.spec.js new file mode 100644 index 0000000000..51c6f81c37 --- /dev/null +++ b/test/chromium/tracing.spec.js @@ -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'); +}); diff --git a/test/electron/electron-app.spec.js b/test/electron/electron-app.spec.js new file mode 100644 index 0000000000..54dcf7384a --- /dev/null +++ b/test/electron/electron-app.spec.js @@ -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,Hello World 1'); + }) + ]); + 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,Hello World 2'); + 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,Hello World ${ordinal}`), 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: 'Hello World', + }) + }); + 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,'); + 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,'); + 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,Hello World!'); + }); + 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); +}); diff --git a/test/electron/electron-window.spec.js b/test/electron/electron-window.spec.js new file mode 100644 index 0000000000..9e80301cc2 --- /dev/null +++ b/test/electron/electron-window.spec.js @@ -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(``); + 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(``); + 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); +}); diff --git a/test/electron/electron.jest.js b/test/electron/electron.jest.js deleted file mode 100644 index f0e972391b..0000000000 --- a/test/electron/electron.jest.js +++ /dev/null @@ -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,Hello World 1'); - }) - ]); - 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,Hello World 2'); - 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,Hello World ${ordinal}`), 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: 'Hello World', - }) - }); - 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,'); - 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,'); - 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,Hello World!'); - }); - 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(``); - await window.check('input'); - expect(await window.evaluate(() => checkbox.checked)).toBe(true); - }); - it('should not check the checked box', async({window}) => { - await window.setContent(``); - 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); - }); -}); diff --git a/test/firefox/launcher.jest.js b/test/firefox/launcher.spec.js similarity index 52% rename from test/firefox/launcher.jest.js rename to test/firefox/launcher.spec.js index acb66ba064..2ed95e6522 100644 --- a/test/firefox/launcher.jest.js +++ b/test/firefox/launcher.spec.js @@ -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(); }); diff --git a/test/fixtures.jest.js b/test/fixtures.jest.js deleted file mode 100644 index f6d6ba0b19..0000000000 --- a/test/fixtures.jest.js +++ /dev/null @@ -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); - }); -}); diff --git a/test/fixtures.spec.js b/test/fixtures.spec.js new file mode 100644 index 0000000000..60f1a5bfcc --- /dev/null +++ b/test/fixtures.spec.js @@ -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); +}); diff --git a/test/launcher.jest.js b/test/launcher.jest.js deleted file mode 100644 index 18fd8545c4..0000000000 --- a/test/launcher.jest.js +++ /dev/null @@ -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] `); - expect(error.message).toContain(`[browser] 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(''); - }); - 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'); - }); -}); diff --git a/test/launcher.spec.js b/test/launcher.spec.js new file mode 100644 index 0000000000..cb742c3936 --- /dev/null +++ b/test/launcher.spec.js @@ -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']); +}); diff --git a/test/page-event-console.spec.js b/test/page-event-console.spec.js index 1e1fbdaf9a..81222fb04d 100644 --- a/test/page-event-console.spec.js +++ b/test/page-event-console.spec.js @@ -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' ),