From 2d4538c23d0170097c0c650403d8dc8210cd7c57 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Sat, 8 May 2021 17:45:04 -0700 Subject: [PATCH] test: cleanup tests and configs after last folio update (#6463) --- tests/{config => android}/androidTest.ts | 2 +- tests/android/browser.spec.ts | 2 +- tests/android/device.spec.ts | 2 +- tests/android/webview.spec.ts | 2 +- tests/chromium/chromium.spec.ts | 414 +++++++------ tests/chromium/css-coverage.spec.ts | 234 ++++---- tests/chromium/js-coverage.spec.ts | 134 +++-- tests/chromium/launcher.spec.ts | 2 - tests/chromium/oopif.spec.ts | 547 +++++++++--------- tests/chromium/session.spec.ts | 208 ++++--- tests/chromium/tracing.spec.ts | 146 +++-- tests/config/android.config.ts | 2 +- tests/config/default.config.ts | 7 +- tests/config/electron.config.ts | 2 +- tests/{config => electron}/electron-app.js | 0 tests/electron/electron-app.spec.ts | 11 +- .../electron-window-app.js | 0 tests/electron/electron-window.spec.ts | 2 +- tests/{config => electron}/electronTest.ts | 3 +- tests/firefox/launcher.spec.ts | 2 - .../{cli => inspector}/cli-codegen-1.spec.ts | 9 +- .../{cli => inspector}/cli-codegen-2.spec.ts | 35 +- .../cli-codegen-csharp.spec.ts | 2 +- .../cli-codegen-java.spec.ts | 2 +- .../cli-codegen-javascript.spec.ts | 2 +- .../cli-codegen-python-async.spec.ts | 2 +- .../cli-codegen-python.spec.ts | 2 +- .../cliTest.ts => inspector/inspectorTest.ts} | 26 +- tests/{ => inspector}/pause.spec.ts | 4 +- 29 files changed, 864 insertions(+), 942 deletions(-) rename tests/{config => android}/androidTest.ts (97%) rename tests/{config => electron}/electron-app.js (100%) rename tests/{config => electron}/electron-window-app.js (100%) rename tests/{config => electron}/electronTest.ts (96%) rename tests/{cli => inspector}/cli-codegen-1.spec.ts (98%) rename tests/{cli => inspector}/cli-codegen-2.spec.ts (95%) rename tests/{cli => inspector}/cli-codegen-csharp.spec.ts (98%) rename tests/{cli => inspector}/cli-codegen-java.spec.ts (98%) rename tests/{cli => inspector}/cli-codegen-javascript.spec.ts (98%) rename tests/{cli => inspector}/cli-codegen-python-async.spec.ts (98%) rename tests/{cli => inspector}/cli-codegen-python.spec.ts (98%) rename tests/{config/cliTest.ts => inspector/inspectorTest.ts} (89%) rename tests/{ => inspector}/pause.spec.ts (99%) diff --git a/tests/config/androidTest.ts b/tests/android/androidTest.ts similarity index 97% rename from tests/config/androidTest.ts rename to tests/android/androidTest.ts index f0125ca86c..fdda9f4cf7 100644 --- a/tests/config/androidTest.ts +++ b/tests/android/androidTest.ts @@ -15,7 +15,7 @@ */ import type { AndroidDevice } from '../../index'; -import { CommonArgs, baseTest } from './baseTest'; +import { CommonArgs, baseTest } from '../config/baseTest'; import * as folio from 'folio'; export { expect } from 'folio'; diff --git a/tests/android/browser.spec.ts b/tests/android/browser.spec.ts index 5e2f5854d1..e577e6da52 100644 --- a/tests/android/browser.spec.ts +++ b/tests/android/browser.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { androidTest as test, expect } from '../config/androidTest'; +import { androidTest as test, expect } from './androidTest'; test('androidDevice.model', async function({ androidDevice }) { expect(androidDevice.model()).toBe('sdk_gphone_x86_arm'); diff --git a/tests/android/device.spec.ts b/tests/android/device.spec.ts index 62e5977527..242a9896d5 100644 --- a/tests/android/device.spec.ts +++ b/tests/android/device.spec.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import { PNG } from 'pngjs'; -import { androidTest as test, expect } from '../config/androidTest'; +import { androidTest as test, expect } from './androidTest'; test('androidDevice.shell', async function({ androidDevice }) { const output = await androidDevice.shell('echo 123'); diff --git a/tests/android/webview.spec.ts b/tests/android/webview.spec.ts index e56eac34e7..34a44752c7 100644 --- a/tests/android/webview.spec.ts +++ b/tests/android/webview.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { androidTest as test, expect } from '../config/androidTest'; +import { androidTest as test, expect } from './androidTest'; test('androidDevice.webView', async function({ androidDevice }) { expect(androidDevice.webViews().length).toBe(0); diff --git a/tests/chromium/chromium.spec.ts b/tests/chromium/chromium.spec.ts index f21326a8ab..67caa90948 100644 --- a/tests/chromium/chromium.spec.ts +++ b/tests/chromium/chromium.spec.ts @@ -19,228 +19,222 @@ import { contextTest as test, expect } from '../config/browserTest'; import { playwrightTest } from '../config/browserTest'; import http from 'http'; -test.describe('chromium', () => { - test.skip(({ browserName }) => browserName !== 'chromium'); +test('should create a worker from a service worker', async ({page, server}) => { + const [worker] = await Promise.all([ + page.context().waitForEvent('serviceworker'), + page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') + ]); + expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); +}); - test('should create a worker from a service worker', async ({page, server}) => { +test('serviceWorkers() should return current workers', async ({page, server}) => { + const context = page.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); +}); + +test('should not create a worker from a shared worker', async ({page, server}) => { + await page.goto(server.EMPTY_PAGE); + let serviceWorkerCreated; + page.context().once('serviceworker', () => serviceWorkerCreated = true); + await page.evaluate(() => { + new SharedWorker('data:text/javascript,console.log("hi")'); + }); + expect(serviceWorkerCreated).not.toBeTruthy(); +}); + +test('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'); +}); + +playwrightTest('should close service worker together with the context', async ({browserType, browserOptions, server}) => { + const browser = await browserType.launch(browserOptions); + 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'); + await browser.close(); +}); + +playwrightTest('should connect to an existing cdp session', async ({ browserType, browserOptions }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + ...browserOptions, + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: `http://localhost:${port}/`, + }); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); + } finally { + await browserServer.close(); + } +}); + +playwrightTest('should connect to an existing cdp session twice', async ({ browserType, browserOptions, server }, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + ...browserOptions, + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser1 = await browserType.connectOverCDP({ + endpointURL: `http://localhost:${port}/`, + }); + const cdpBrowser2 = await browserType.connectOverCDP({ + endpointURL: `http://localhost:${port}/`, + }); + const contexts1 = cdpBrowser1.contexts(); + expect(contexts1.length).toBe(1); + const [context1] = contexts1; + const page1 = await context1.newPage(); + await page1.goto(server.EMPTY_PAGE); + + const contexts2 = cdpBrowser2.contexts(); + expect(contexts2.length).toBe(1); + const [context2] = contexts2; + const page2 = await context2.newPage(); + await page2.goto(server.EMPTY_PAGE); + + expect(context1.pages().length).toBe(2); + expect(context2.pages().length).toBe(2); + + await cdpBrowser1.close(); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } +}); + +playwrightTest('should connect to existing service workers', async ({browserType, browserOptions, server}, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + ...browserOptions, + args: ['--remote-debugging-port=' + port] + }); + try { + const cdpBrowser1 = await browserType.connectOverCDP({ + endpointURL: `http://localhost:${port}`, + }); + const context = cdpBrowser1.contexts()[0]; + const page = await cdpBrowser1.contexts()[0].newPage(); const [worker] = await Promise.all([ - page.context().waitForEvent('serviceworker'), + context.waitForEvent('serviceworker'), page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') ]); expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); - }); + await cdpBrowser1.close(); - test('serviceWorkers() should return current workers', async ({page, server}) => { - const context = page.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); - }); - - test('should not create a worker from a shared worker', async ({page, server}) => { - await page.goto(server.EMPTY_PAGE); - let serviceWorkerCreated; - page.context().once('serviceworker', () => serviceWorkerCreated = true); - await page.evaluate(() => { - new SharedWorker('data:text/javascript,console.log("hi")'); + const cdpBrowser2 = await browserType.connectOverCDP({ + endpointURL: `http://localhost:${port}`, }); - expect(serviceWorkerCreated).not.toBeTruthy(); - }); - - test('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'); - }); + const context2 = cdpBrowser2.contexts()[0]; + expect(context2.serviceWorkers().length).toBe(1); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } }); -playwrightTest.describe('chromium', () => { - playwrightTest.skip(({ browserName }) => browserName !== 'chromium'); - - playwrightTest('should close service worker together with the context', async ({browserType, browserOptions, server}) => { - const browser = await browserType.launch(browserOptions); - 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'); - await browser.close(); +playwrightTest('should connect over a ws endpoint', async ({browserType, browserOptions, server}, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + ...browserOptions, + args: ['--remote-debugging-port=' + port] }); - - playwrightTest('should connect to an existing cdp session', async ({ browserType, browserOptions }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - ...browserOptions, - args: ['--remote-debugging-port=' + port] + try { + const json = await new Promise((resolve, reject) => { + http.get(`http://localhost:${port}/json/version/`, resp => { + let data = ''; + resp.on('data', chunk => data += chunk); + resp.on('end', () => resolve(data)); + }).on('error', reject); }); - try { - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, - }); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await cdpBrowser.close(); - } finally { - await browserServer.close(); - } - }); - - playwrightTest('should connect to an existing cdp session twice', async ({ browserType, browserOptions, server }, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - ...browserOptions, - args: ['--remote-debugging-port=' + port] + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: JSON.parse(json).webSocketDebuggerUrl, }); - try { - const cdpBrowser1 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, - }); - const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, - }); - const contexts1 = cdpBrowser1.contexts(); - expect(contexts1.length).toBe(1); - const [context1] = contexts1; - const page1 = await context1.newPage(); - await page1.goto(server.EMPTY_PAGE); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); - const contexts2 = cdpBrowser2.contexts(); - expect(contexts2.length).toBe(1); - const [context2] = contexts2; - const page2 = await context2.newPage(); - await page2.goto(server.EMPTY_PAGE); - - expect(context1.pages().length).toBe(2); - expect(context2.pages().length).toBe(2); - - await cdpBrowser1.close(); - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } - }); - - playwrightTest('should connect to existing service workers', async ({browserType, browserOptions, server}, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - ...browserOptions, - args: ['--remote-debugging-port=' + port] + // also connect with the depercreated wsEndpoint option + const cdpBrowser2 = await browserType.connectOverCDP({ + wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, }); - try { - const cdpBrowser1 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}`, - }); - const context = cdpBrowser1.contexts()[0]; - const page = await cdpBrowser1.contexts()[0].newPage(); - 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]'); - await cdpBrowser1.close(); - - const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}`, - }); - const context2 = cdpBrowser2.contexts()[0]; - expect(context2.serviceWorkers().length).toBe(1); - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } - }); - playwrightTest('should connect over a ws endpoint', async ({browserType, browserOptions, server}, testInfo) => { - const port = 9339 + testInfo.workerIndex; - const browserServer = await browserType.launch({ - ...browserOptions, - args: ['--remote-debugging-port=' + port] - }); - try { - const json = await new Promise((resolve, reject) => { - http.get(`http://localhost:${port}/json/version/`, resp => { - let data = ''; - resp.on('data', chunk => data += chunk); - resp.on('end', () => resolve(data)); - }).on('error', reject); - }); - const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: JSON.parse(json).webSocketDebuggerUrl, - }); - const contexts = cdpBrowser.contexts(); - expect(contexts.length).toBe(1); - await cdpBrowser.close(); - - // also connect with the depercreated wsEndpoint option - const cdpBrowser2 = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, - }); - const contexts2 = cdpBrowser.contexts(); - expect(contexts2.length).toBe(1); - await cdpBrowser2.close(); - } finally { - await browserServer.close(); - } - }); - playwrightTest('should send extra headers with connect request', async ({browserType, browserOptions, server}, testInfo) => { - { - const [request] = await Promise.all([ - server.waitForWebSocketConnectionRequest(), - browserType.connectOverCDP({ - wsEndpoint: `ws://localhost:${server.PORT}/ws`, - headers: { - 'User-Agent': 'Playwright', - 'foo': 'bar', - } - }).catch(() => {}) - ]); - expect(request.headers['user-agent']).toBe('Playwright'); - expect(request.headers['foo']).toBe('bar'); - } - { - const [request] = await Promise.all([ - server.waitForWebSocketConnectionRequest(), - browserType.connectOverCDP({ - endpointURL: `ws://localhost:${server.PORT}/ws`, - headers: { - 'User-Agent': 'Playwright', - 'foo': 'bar', - } - }).catch(() => {}) - ]); - expect(request.headers['user-agent']).toBe('Playwright'); - expect(request.headers['foo']).toBe('bar'); - } - }); + const contexts2 = cdpBrowser.contexts(); + expect(contexts2.length).toBe(1); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } +}); + +playwrightTest('should send extra headers with connect request', async ({browserType, browserOptions, server}, testInfo) => { + { + const [request] = await Promise.all([ + server.waitForWebSocketConnectionRequest(), + browserType.connectOverCDP({ + wsEndpoint: `ws://localhost:${server.PORT}/ws`, + headers: { + 'User-Agent': 'Playwright', + 'foo': 'bar', + } + }).catch(() => {}) + ]); + expect(request.headers['user-agent']).toBe('Playwright'); + expect(request.headers['foo']).toBe('bar'); + } + { + const [request] = await Promise.all([ + server.waitForWebSocketConnectionRequest(), + browserType.connectOverCDP({ + endpointURL: `ws://localhost:${server.PORT}/ws`, + headers: { + 'User-Agent': 'Playwright', + 'foo': 'bar', + } + }).catch(() => {}) + ]); + expect(request.headers['user-agent']).toBe('Playwright'); + expect(request.headers['foo']).toBe('bar'); + } }); diff --git a/tests/chromium/css-coverage.spec.ts b/tests/chromium/css-coverage.spec.ts index a041bdcdb9..5f78a9dd62 100644 --- a/tests/chromium/css-coverage.spec.ts +++ b/tests/chromium/css-coverage.spec.ts @@ -16,123 +16,119 @@ import { contextTest as it, expect } from '../config/browserTest'; -it.describe('CSS Coverage', () => { - it.skip(({ browserName }) => browserName !== 'chromium'); - - it('should work', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/simple.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/csscoverage/simple.html'); - expect(coverage[0].ranges).toEqual([ - {start: 1, end: 22} - ]); - const range = coverage[0].ranges[0]; - expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }'); - }); - - it('should report sourceURLs', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('nicename.css'); - }); - - it('should report multiple stylesheets', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/multiple.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(2); - coverage.sort((a, b) => a.url.localeCompare(b.url)); - expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); - expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); - }); - - it('should report stylesheets that have no coverage', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/unused.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('unused.css'); - expect(coverage[0].ranges.length).toBe(0); - }); - - it('should work with media queries', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/media.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/csscoverage/media.html'); - expect(coverage[0].ranges).toEqual([ - {start: 17, end: 38} - ]); - }); - - it('should work with complicated usecases', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/involved.html'); - const coverage: any = await page.coverage.stopCSSCoverage(); - delete coverage[0].text; - delete coverage[0].url; - expect(coverage).toEqual( - [ - { - 'ranges': [ - { - 'start': 149, - 'end': 297 - }, - { - 'start': 327, - 'end': 433 - } - ] - } - ] - ); - }); - - it('should ignore injected stylesheets', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.addStyleTag({content: 'body { margin: 10px;}'}); - // trigger style recalc - const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin); - expect(margin).toBe('10px'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(0); - }); - - it('should report stylesheets across navigations', async function({page, server}) { - await page.coverage.startCSSCoverage({resetOnNavigation: false}); - await page.goto(server.PREFIX + '/csscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(2); - }); - - it('should NOT report scripts across navigations', async function({page, server}) { - await page.coverage.startCSSCoverage(); // Enabled by default. - await page.goto(server.PREFIX + '/csscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(0); - }); - - it('should work with a recently loaded stylesheet', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.evaluate(async url => { - document.body.textContent = 'hello, world'; - - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = url; - document.head.appendChild(link); - await new Promise(x => link.onload = x); - await new Promise(f => requestAnimationFrame(f)); - }, server.PREFIX + '/csscoverage/stylesheet1.css'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - }); +it('should work', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/simple.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toContain('/csscoverage/simple.html'); + expect(coverage[0].ranges).toEqual([ + {start: 1, end: 22} + ]); + const range = coverage[0].ranges[0]; + expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }'); +}); + +it('should report sourceURLs', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toBe('nicename.css'); +}); + +it('should report multiple stylesheets', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/multiple.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(2); + coverage.sort((a, b) => a.url.localeCompare(b.url)); + expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); + expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); +}); + +it('should report stylesheets that have no coverage', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/unused.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toBe('unused.css'); + expect(coverage[0].ranges.length).toBe(0); +}); + +it('should work with media queries', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/media.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toContain('/csscoverage/media.html'); + expect(coverage[0].ranges).toEqual([ + {start: 17, end: 38} + ]); +}); + +it('should work with complicated usecases', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/involved.html'); + const coverage: any = await page.coverage.stopCSSCoverage(); + delete coverage[0].text; + delete coverage[0].url; + expect(coverage).toEqual( + [ + { + 'ranges': [ + { + 'start': 149, + 'end': 297 + }, + { + 'start': 327, + 'end': 433 + } + ] + } + ] + ); +}); + +it('should ignore injected stylesheets', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.addStyleTag({content: 'body { margin: 10px;}'}); + // trigger style recalc + const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin); + expect(margin).toBe('10px'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(0); +}); + +it('should report stylesheets across navigations', async function({page, server}) { + await page.coverage.startCSSCoverage({resetOnNavigation: false}); + await page.goto(server.PREFIX + '/csscoverage/multiple.html'); + await page.goto(server.EMPTY_PAGE); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(2); +}); + +it('should NOT report scripts across navigations', async function({page, server}) { + await page.coverage.startCSSCoverage(); // Enabled by default. + await page.goto(server.PREFIX + '/csscoverage/multiple.html'); + await page.goto(server.EMPTY_PAGE); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(0); +}); + +it('should work with a recently loaded stylesheet', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.evaluate(async url => { + document.body.textContent = 'hello, world'; + + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = url; + document.head.appendChild(link); + await new Promise(x => link.onload = x); + await new Promise(f => requestAnimationFrame(f)); + }, server.PREFIX + '/csscoverage/stylesheet1.css'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); }); diff --git a/tests/chromium/js-coverage.spec.ts b/tests/chromium/js-coverage.spec.ts index 42d94605bd..5a9bebe0b6 100644 --- a/tests/chromium/js-coverage.spec.ts +++ b/tests/chromium/js-coverage.spec.ts @@ -16,73 +16,69 @@ import { contextTest as it, expect } from '../config/browserTest'; -it.describe('JS Coverage', () => { - it.skip(({ browserName }) => browserName !== 'chromium'); - - it('should work', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/jscoverage/simple.html'); - expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1); - }); - - it('should report sourceURLs', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('nicename.js'); - }); - - it('should ignore eval() scripts by default', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/eval.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(1); - }); - - it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) { - await page.coverage.startJSCoverage({reportAnonymousScripts: true}); - await page.goto(server.PREFIX + '/jscoverage/eval.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.find(entry => entry.url === '').source).toBe('console.log("foo")'); - expect(coverage.length).toBe(2); - }); - - it('should report multiple scripts', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/multiple.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(2); - coverage.sort((a, b) => a.url.localeCompare(b.url)); - expect(coverage[0].url).toContain('/jscoverage/script1.js'); - expect(coverage[1].url).toContain('/jscoverage/script2.js'); - }); - - it('should report scripts across navigations when disabled', async function({page, server}) { - await page.coverage.startJSCoverage({resetOnNavigation: false}); - await page.goto(server.PREFIX + '/jscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(2); - }); - - it('should NOT report scripts across navigations when enabled', async function({page, server}) { - await page.coverage.startJSCoverage(); // Enabled by default. - await page.goto(server.PREFIX + '/jscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(0); - }); - - it('should not hang when there is a debugger statement', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => { - debugger; // eslint-disable-line no-debugger - }); - await page.coverage.stopJSCoverage(); - }); +it('should work', async function({page, server}) { + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toContain('/jscoverage/simple.html'); + expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1); +}); + +it('should report sourceURLs', async function({page, server}) { + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toBe('nicename.js'); +}); + +it('should ignore eval() scripts by default', async function({page, server}) { + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/eval.html'); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(1); +}); + +it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) { + await page.coverage.startJSCoverage({reportAnonymousScripts: true}); + await page.goto(server.PREFIX + '/jscoverage/eval.html'); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.find(entry => entry.url === '').source).toBe('console.log("foo")'); + expect(coverage.length).toBe(2); +}); + +it('should report multiple scripts', async function({page, server}) { + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/multiple.html'); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(2); + coverage.sort((a, b) => a.url.localeCompare(b.url)); + expect(coverage[0].url).toContain('/jscoverage/script1.js'); + expect(coverage[1].url).toContain('/jscoverage/script2.js'); +}); + +it('should report scripts across navigations when disabled', async function({page, server}) { + await page.coverage.startJSCoverage({resetOnNavigation: false}); + await page.goto(server.PREFIX + '/jscoverage/multiple.html'); + await page.goto(server.EMPTY_PAGE); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(2); +}); + +it('should NOT report scripts across navigations when enabled', async function({page, server}) { + await page.coverage.startJSCoverage(); // Enabled by default. + await page.goto(server.PREFIX + '/jscoverage/multiple.html'); + await page.goto(server.EMPTY_PAGE); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(0); +}); + +it('should not hang when there is a debugger statement', async function({page, server}) { + await page.coverage.startJSCoverage(); + await page.goto(server.EMPTY_PAGE); + await page.evaluate(() => { + debugger; // eslint-disable-line no-debugger + }); + await page.coverage.stopJSCoverage(); }); diff --git a/tests/chromium/launcher.spec.ts b/tests/chromium/launcher.spec.ts index 8574a822b4..612eb8429d 100644 --- a/tests/chromium/launcher.spec.ts +++ b/tests/chromium/launcher.spec.ts @@ -16,8 +16,6 @@ import { playwrightTest as it, expect } from '../config/browserTest'; -it.skip(({ browserName }) => browserName !== 'chromium'); - it('should throw with remote-debugging-pipe argument', async ({browserType, browserOptions, mode}) => { it.skip(mode !== 'default'); diff --git a/tests/chromium/oopif.spec.ts b/tests/chromium/oopif.spec.ts index b5bbef7624..13617edb9d 100644 --- a/tests/chromium/oopif.spec.ts +++ b/tests/chromium/oopif.spec.ts @@ -14,307 +14,282 @@ * limitations under the License. */ -import { playwrightTest, expect } from '../config/browserTest'; -import type { Page } from '../../index'; +import { contextTest as it, expect } from '../config/browserTest'; -const it = playwrightTest.extend({ - async beforeAll({ browserType, browserOptions, browserName }) { - const browser = await browserType.launch({ - ...browserOptions, - args: browserName === 'chromium' ? (browserOptions.args || []).concat(['--site-per-process']) : browserOptions.args, - }); - this.browser = browser; - return { browser }; - }, - async beforeEach() { - const page = await this.browser.newPage() as Page; - this.page = page; - return { page }; - }, - async afterEach() { - await this.page.close(); - }, - async afterAll() { - await this.browser.close(); - }, +it.useOptions({ launchOptions: { args: ['--site-per-process'] } }); + +it('should report oopif frames', async function({page, browser, server}) { + 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.describe('oopif', () => { - it.skip(({ browserName }) => browserName !== 'chromium'); +it('should handle oopif detach', async function({page, browser, server}) { + 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 report oopif frames', async function({page, browser, server}) { - 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 remote -> local -> remote transitions', async function({page, browser, server}) { + 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('should get the proper viewport', async ({page, browser, server}) => { + it.fixme(); + + 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 ({page, browser, server}) => { + 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 window['mul'](9, 4); }); + expect(result).toBe(36); +}); - it('should handle oopif detach', async function({page, browser, server}) { - 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 emulate media', async ({page, browser, server}) => { + 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 ({page, browser, server}) => { + 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 page.context().setOffline(true); + expect(await oopif.evaluate(() => navigator.onLine)).toBe(false); +}); + +it('should support context options', async ({browser, server, playwright}) => { + 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 ({page, browser, server}) => { + 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 handle remote -> local -> remote transitions', async function({page, browser, server}) { - 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('should take screenshot', async ({page, browser, server}) => { + 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()).toMatchSnapshot('screenshot-oopif.png', { threshold: 0.3 }); +}); + +it('should load oopif iframes with subresources and route', async function({page, browser, server}) { + 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({page, browser, server}) { + 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({page, browser, server}) { + await page.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(() => window['inc'](3))).toBe(4); + expect(await page.frames()[1].evaluate(() => window['inc'](4))).toBe(5); + expect(await page.frames()[0].evaluate(() => window['dec'](3))).toBe(2); + expect(await page.frames()[1].evaluate(() => window['dec'](4))).toBe(3); +}); + +it('should support addInitScript', async function({page, browser, server}) { + await page.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({page, browser, server}) { + 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, browserOptions, server}) => { + // @see https://github.com/GoogleChrome/puppeteer/issues/2548 + // https://google.com is isolated by default in Chromium embedder. + const browser = await browserType.launch({...browserOptions, headless: false}); + const page = await browser.newPage(); + await page.goto(server.EMPTY_PAGE); + await page.route('**/*', route => { + route.fulfill({body: 'YO, GOOGLE.COM'}); }); - - it('should get the proper viewport', async ({page, browser, server}) => { - it.fixme(); - - 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); + 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('should expose function', async ({page, browser, server}) => { - 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 window['mul'](9, 4); - }); - expect(result).toBe(36); +it('ElementHandle.boundingBox() should work', async function({page, browser, server}) { + 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()); - it('should emulate media', async ({page, browser, server}) => { - 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); + 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({page, browser, server}) { + 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()); - it('should emulate offline', async ({page, browser, server}) => { - 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 page.context().setOffline(true); - expect(await oopif.evaluate(() => navigator.onLine)).toBe(false); - }); - - it('should support context options', async ({browser, server, playwright}) => { - 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 ({page, browser, server}) => { - 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 ({page, browser, server}) => { - 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()).toMatchSnapshot('screenshot-oopif.png', { threshold: 0.3 }); - }); - - it('should load oopif iframes with subresources and route', async function({page, browser, server}) { - 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({page, browser, server}) { - 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({page, browser, server}) { - await page.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(() => window['inc'](3))).toBe(4); - expect(await page.frames()[1].evaluate(() => window['inc'](4))).toBe(5); - expect(await page.frames()[0].evaluate(() => window['dec'](3))).toBe(2); - expect(await page.frames()[1].evaluate(() => window['dec'](4))).toBe(3); - }); - - it('should support addInitScript', async function({page, browser, server}) { - await page.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({page, browser, server}) { - 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, browserOptions, server}) => { - // @see https://github.com/GoogleChrome/puppeteer/issues/2548 - // https://google.com is isolated by default in Chromium embedder. - const browser = await browserType.launch({...browserOptions, 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({page, browser, server}) { - 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({page, browser, server}) { - 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); - }); + 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) { diff --git a/tests/chromium/session.spec.ts b/tests/chromium/session.spec.ts index 74e61f8bdf..e9da7cbdc3 100644 --- a/tests/chromium/session.spec.ts +++ b/tests/chromium/session.spec.ts @@ -17,115 +17,107 @@ import { contextTest as it, expect } from '../config/browserTest'; import { browserTest } from '../config/browserTest'; -it.describe('session', () => { - it.skip(({ browserName }) => browserName !== 'chromium'); +it('should work', async function({page}) { + const client = await page.context().newCDPSession(page); - 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 only accept a page', async function({page}) { - // @ts-expect-error newCDPSession expects a Page - const error = await page.context().newCDPSession(page.context()).catch(e => e); - expect(error.message).toContain('page: expected Page'); - }); - - 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('Target page, context or browser has been 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() { - // @ts-expect-error invalid command - await client.send('ThisCommand.DoesNotExist'); - } - }); + await Promise.all([ + client.send('Runtime.enable'), + client.send('Runtime.evaluate', { expression: 'window.foo = "bar"' }) + ]); + const foo = await page.evaluate(() => window['foo']); + expect(foo).toBe('bar'); }); -browserTest.describe('session', () => { - browserTest.skip(({ browserName }) => browserName !== 'chromium'); - - browserTest('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(); - }); - - browserTest('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(); - }); - - browserTest('should work with newBrowserCDPSession', 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(); - }); +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 only accept a page', async function({page}) { + // @ts-expect-error newCDPSession expects a Page + const error = await page.context().newCDPSession(page.context()).catch(e => e); + expect(error.message).toContain('page: expected Page'); +}); + +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('Target page, context or browser has been 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() { + // @ts-expect-error invalid command + await client.send('ThisCommand.DoesNotExist'); + } +}); + +browserTest('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(); +}); + +browserTest('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(); +}); + +browserTest('should work with newBrowserCDPSession', 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/tests/chromium/tracing.spec.ts b/tests/chromium/tracing.spec.ts index 3e2bf87de3..85b05c4a1f 100644 --- a/tests/chromium/tracing.spec.ts +++ b/tests/chromium/tracing.spec.ts @@ -18,79 +18,75 @@ import { browserTest as it, expect } from '../config/browserTest'; import fs from 'fs'; import path from 'path'; -it.describe('tracing', () => { - it.skip(({ browserName }) => browserName !== 'chromium'); - - it('should output a trace', async ({browser, server}, testInfo) => { - const page = await browser.newPage(); - const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await browser.startTracing(page, {screenshots: true, path: outputTraceFile}); - await page.goto(server.PREFIX + '/grid.html'); - await browser.stopTracing(); - expect(fs.existsSync(outputTraceFile)).toBe(true); - await page.close(); - }); - - it('should create directories as needed', async ({browser, server}, testInfo) => { - const page = await browser.newPage(); - const filePath = testInfo.outputPath(path.join('these', 'are', 'directories', 'trace.json')); - await browser.startTracing(page, {screenshots: true, path: filePath}); - await page.goto(server.PREFIX + '/grid.html'); - await browser.stopTracing(); - expect(fs.existsSync(filePath)).toBe(true); - await page.close(); - }); - - it('should run with custom categories if provided', async ({browser}, testInfo) => { - const page = await browser.newPage(); - const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await browser.startTracing(page, {path: outputTraceFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); - await browser.stopTracing(); - - const traceJson = JSON.parse(fs.readFileSync(outputTraceFile).toString()); - expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); - await page.close(); - }); - - it('should throw if tracing on two pages', async ({browser}, testInfo) => { - const page = await browser.newPage(); - const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await browser.startTracing(page, {path: outputTraceFile}); - const newPage = await browser.newPage(); - let error = null; - await browser.startTracing(newPage, {path: outputTraceFile}).catch(e => error = e); - await newPage.close(); - expect(error).toBeTruthy(); - await browser.stopTracing(); - await page.close(); - }); - - it('should return a buffer', async ({browser, server}, testInfo) => { - const page = await browser.newPage(); - const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); - await browser.startTracing(page, {screenshots: true, path: outputTraceFile}); - await page.goto(server.PREFIX + '/grid.html'); - const trace = await browser.stopTracing(); - const buf = fs.readFileSync(outputTraceFile); - expect(trace.toString()).toEqual(buf.toString()); - await page.close(); - }); - - it('should work without options', async ({browser, server}) => { - const page = await browser.newPage(); - await browser.startTracing(page); - await page.goto(server.PREFIX + '/grid.html'); - const trace = await browser.stopTracing(); - expect(trace).toBeTruthy(); - await page.close(); - }); - - it('should support a buffer without a path', async ({browser, server}) => { - const page = await browser.newPage(); - await browser.startTracing(page, {screenshots: true}); - await page.goto(server.PREFIX + '/grid.html'); - const trace = await browser.stopTracing(); - expect(trace.toString()).toContain('screenshot'); - await page.close(); - }); +it('should output a trace', async ({browser, server}, testInfo) => { + const page = await browser.newPage(); + const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); + await browser.startTracing(page, {screenshots: true, path: outputTraceFile}); + await page.goto(server.PREFIX + '/grid.html'); + await browser.stopTracing(); + expect(fs.existsSync(outputTraceFile)).toBe(true); + await page.close(); +}); + +it('should create directories as needed', async ({browser, server}, testInfo) => { + const page = await browser.newPage(); + const filePath = testInfo.outputPath(path.join('these', 'are', 'directories', 'trace.json')); + await browser.startTracing(page, {screenshots: true, path: filePath}); + await page.goto(server.PREFIX + '/grid.html'); + await browser.stopTracing(); + expect(fs.existsSync(filePath)).toBe(true); + await page.close(); +}); + +it('should run with custom categories if provided', async ({browser}, testInfo) => { + const page = await browser.newPage(); + const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); + await browser.startTracing(page, {path: outputTraceFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); + await browser.stopTracing(); + + const traceJson = JSON.parse(fs.readFileSync(outputTraceFile).toString()); + expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); + await page.close(); +}); + +it('should throw if tracing on two pages', async ({browser}, testInfo) => { + const page = await browser.newPage(); + const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); + await browser.startTracing(page, {path: outputTraceFile}); + const newPage = await browser.newPage(); + let error = null; + await browser.startTracing(newPage, {path: outputTraceFile}).catch(e => error = e); + await newPage.close(); + expect(error).toBeTruthy(); + await browser.stopTracing(); + await page.close(); +}); + +it('should return a buffer', async ({browser, server}, testInfo) => { + const page = await browser.newPage(); + const outputTraceFile = testInfo.outputPath(path.join(`trace.json`)); + await browser.startTracing(page, {screenshots: true, path: outputTraceFile}); + await page.goto(server.PREFIX + '/grid.html'); + const trace = await browser.stopTracing(); + const buf = fs.readFileSync(outputTraceFile); + expect(trace.toString()).toEqual(buf.toString()); + await page.close(); +}); + +it('should work without options', async ({browser, server}) => { + const page = await browser.newPage(); + await browser.startTracing(page); + await page.goto(server.PREFIX + '/grid.html'); + const trace = await browser.stopTracing(); + expect(trace).toBeTruthy(); + await page.close(); +}); + +it('should support a buffer without a path', async ({browser, server}) => { + const page = await browser.newPage(); + await browser.startTracing(page, {screenshots: true}); + await page.goto(server.PREFIX + '/grid.html'); + const trace = await browser.stopTracing(); + expect(trace.toString()).toContain('screenshot'); + await page.close(); }); diff --git a/tests/config/android.config.ts b/tests/config/android.config.ts index f78eed7de4..0fe774bbaf 100644 --- a/tests/config/android.config.ts +++ b/tests/config/android.config.ts @@ -17,7 +17,7 @@ import * as folio from 'folio'; import * as path from 'path'; import { test as pageTest } from '../page/pageTest'; -import { AndroidEnv } from './androidTest'; +import { AndroidEnv } from '../android/androidTest'; import type { BrowserContext } from '../../index'; import { PlaywrightEnvOptions } from './browserTest'; import { CommonOptions } from './baseTest'; diff --git a/tests/config/default.config.ts b/tests/config/default.config.ts index 711715c991..433e5ec34a 100644 --- a/tests/config/default.config.ts +++ b/tests/config/default.config.ts @@ -95,14 +95,17 @@ const config: folio.Config = { projects: [], }; -for (const browserName of ['chromium', 'webkit', 'firefox'] as BrowserName[]) { +const browserNames = ['chromium', 'webkit', 'firefox'] as BrowserName[]; +for (const browserName of browserNames) { const executablePath = getExecutablePath(browserName); if (executablePath && (process.env.FOLIO_WORKER_INDEX === undefined || process.env.FOLIO_WORKER_INDEX === '')) console.error(`Using executable at ${executablePath}`); + const testIgnore: RegExp[] = browserNames.filter(b => b !== browserName).map(b => new RegExp(b)); + testIgnore.push(/android/, /electron/); config.projects.push({ name: browserName, testDir, - testIgnore: [/android/, /electron/], + testIgnore, options: { mode: (process.env.PWTEST_MODE || 'default') as ('default' | 'driver' | 'service'), browserName, diff --git a/tests/config/electron.config.ts b/tests/config/electron.config.ts index 6f5cf32343..3babc06902 100644 --- a/tests/config/electron.config.ts +++ b/tests/config/electron.config.ts @@ -16,7 +16,7 @@ import * as folio from 'folio'; import * as path from 'path'; -import { ElectronEnv } from './electronTest'; +import { ElectronEnv } from '../electron/electronTest'; import { test as pageTest } from '../page/pageTest'; import { PlaywrightEnvOptions } from './browserTest'; import { CommonOptions } from './baseTest'; diff --git a/tests/config/electron-app.js b/tests/electron/electron-app.js similarity index 100% rename from tests/config/electron-app.js rename to tests/electron/electron-app.js diff --git a/tests/electron/electron-app.spec.ts b/tests/electron/electron-app.spec.ts index 1c07c78d7a..6ceed9f54d 100644 --- a/tests/electron/electron-app.spec.ts +++ b/tests/electron/electron-app.spec.ts @@ -16,11 +16,12 @@ import type { BrowserWindow } from 'electron'; import path from 'path'; -import { electronTest as test, baseElectronTest as baseTest, expect } from '../config/electronTest'; +import { electronTest as test, expect } from './electronTest'; +import { baseTest } from '../config/baseTest'; baseTest('should fire close event', async ({ playwright }) => { const electronApp = await playwright._electron.launch({ - args: [path.join(__dirname, '..', 'config', 'electron-app.js')], + args: [path.join(__dirname, 'electron-app.js')], }); const events = []; electronApp.on('close', () => events.push('application')); @@ -34,7 +35,7 @@ baseTest('should fire close event', async ({ playwright }) => { test('should script application', async ({ electronApp }) => { const appPath = await electronApp.evaluate(async ({ app }) => app.getAppPath()); - expect(appPath).toBe(path.resolve(__dirname, '..', 'config')); + expect(appPath).toBe(path.resolve(__dirname)); }); test('should return windows', async ({ electronApp, newWindow }) => { @@ -92,14 +93,14 @@ test('should have a clipboard instance', async ({ electronApp }) => { test('should test app that opens window fast', async ({ playwright }) => { const electronApp = await playwright._electron.launch({ - args: [path.join(__dirname, '..', 'config', 'electron-window-app.js')], + args: [path.join(__dirname, 'electron-window-app.js')], }); await electronApp.close(); }); test('should return browser window', async ({ playwright }) => { const electronApp = await playwright._electron.launch({ - args: [path.join(__dirname, '..', 'config', 'electron-window-app.js')], + args: [path.join(__dirname, 'electron-window-app.js')], }); const page = await electronApp.waitForEvent('window'); const bwHandle = await electronApp.browserWindow(page); diff --git a/tests/config/electron-window-app.js b/tests/electron/electron-window-app.js similarity index 100% rename from tests/config/electron-window-app.js rename to tests/electron/electron-window-app.js diff --git a/tests/electron/electron-window.spec.ts b/tests/electron/electron-window.spec.ts index 7fb4982a7a..14b5256335 100644 --- a/tests/electron/electron-window.spec.ts +++ b/tests/electron/electron-window.spec.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { electronTest as test, expect } from '../config/electronTest'; +import { electronTest as test, expect } from './electronTest'; test('should click the button', async ({newWindow, server}) => { const window = await newWindow(); diff --git a/tests/config/electronTest.ts b/tests/electron/electronTest.ts similarity index 96% rename from tests/config/electronTest.ts rename to tests/electron/electronTest.ts index cd749f8827..2bd92e30c0 100644 --- a/tests/config/electronTest.ts +++ b/tests/electron/electronTest.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { baseTest, CommonArgs } from './baseTest'; +import { baseTest, CommonArgs } from '../config/baseTest'; import { ElectronApplication, Page } from '../../index'; import * as folio from 'folio'; import * as path from 'path'; @@ -79,5 +79,4 @@ export class ElectronEnv { } } -export const baseElectronTest = baseTest; export const electronTest = baseTest.extend(new ElectronEnv()); diff --git a/tests/firefox/launcher.spec.ts b/tests/firefox/launcher.spec.ts index adc7741d44..2b53c4c2f3 100644 --- a/tests/firefox/launcher.spec.ts +++ b/tests/firefox/launcher.spec.ts @@ -17,8 +17,6 @@ import { playwrightTest as it, expect } from '../config/browserTest'; it('should pass firefox user preferences', async ({browserType, browserOptions, browserName}) => { - it.skip(browserName !== 'firefox'); - const browser = await browserType.launch({ ...browserOptions, firefoxUserPrefs: { diff --git a/tests/cli/cli-codegen-1.spec.ts b/tests/inspector/cli-codegen-1.spec.ts similarity index 98% rename from tests/cli/cli-codegen-1.spec.ts rename to tests/inspector/cli-codegen-1.spec.ts index 59fb37c1bb..1f46510a0c 100644 --- a/tests/cli/cli-codegen-1.spec.ts +++ b/tests/inspector/cli-codegen-1.spec.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { cliTest as test, expect } from '../config/cliTest'; -import * as http from 'http'; +import { test, expect } from './inspectorTest'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); @@ -58,14 +57,14 @@ await page.ClickAsync("text=Submit");`); expect(message.text()).toBe('click'); }); - test('should click after same-document navigation', async ({ page, openRecorder, httpServer }) => { + test('should click after same-document navigation', async ({ page, openRecorder, server }) => { const recorder = await openRecorder(); - httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => { + server.setRoute('/foo.html', (req, res) => { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end(''); }); - await recorder.setContentAndWait(``, httpServer.PREFIX + '/foo.html'); + await recorder.setContentAndWait(``, server.PREFIX + '/foo.html'); await Promise.all([ page.waitForNavigation(), page.evaluate(() => history.pushState({}, '', '/url.html')), diff --git a/tests/cli/cli-codegen-2.spec.ts b/tests/inspector/cli-codegen-2.spec.ts similarity index 95% rename from tests/cli/cli-codegen-2.spec.ts rename to tests/inspector/cli-codegen-2.spec.ts index f83e3699b9..12f0019133 100644 --- a/tests/cli/cli-codegen-2.spec.ts +++ b/tests/inspector/cli-codegen-2.spec.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { cliTest as test, expect } from '../config/cliTest'; -import * as http from 'http'; +import { test, expect } from './inspectorTest'; import * as url from 'url'; test.describe('cli codegen', () => { @@ -228,10 +227,10 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`); }); - test('should download files', async ({ page, openRecorder, httpServer }) => { + test('should download files', async ({ page, openRecorder, server }) => { const recorder = await openRecorder(); - httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => { + server.setRoute('/download', (req, res) => { const pathName = url.parse(req.url!).path; if (pathName === '/download') { res.setHeader('Content-Type', 'application/octet-stream'); @@ -243,8 +242,8 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`); } }); await recorder.setContentAndWait(` - Download - `, httpServer.PREFIX); + Download + `, server.PREFIX); await recorder.hoverOverElement('text=Download'); await Promise.all([ page.waitForEvent('download'), @@ -338,23 +337,19 @@ await page.ClickAsync(\"text=click me\");`); }); - test('should handle history.postData', async ({ page, openRecorder, httpServer }) => { + test('should handle history.postData', async ({ page, openRecorder, server }) => { const recorder = await openRecorder(); - httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => { - res.setHeader('Content-Type', 'text/html; charset=utf-8'); - res.end('Hello world'); - }); await recorder.setContentAndWait(` `, httpServer.PREFIX); + `, server.PREFIX); for (let i = 1; i < 3; ++i) { await page.evaluate('pushState()'); - await recorder.waitForOutput('', `await page.goto('${httpServer.PREFIX}/#seqNum=${i}');`); + await recorder.waitForOutput('', `await page.goto('${server.PREFIX}/#seqNum=${i}');`); } }); @@ -581,23 +576,23 @@ await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`); await page.GetFrame(url: \"http://localhost:${server.PORT}/frames/frame.html\").ClickAsync(\"text=Hi, I'm frame\");`); }); - test('should record navigations after identical pushState', async ({ page, openRecorder, httpServer }) => { + test('should record navigations after identical pushState', async ({ page, openRecorder, server }) => { const recorder = await openRecorder(); - httpServer.setHandler((req: http.IncomingMessage, res: http.ServerResponse) => { + server.setRoute('/page2.html', (req, res) => { res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end('Hello world'); }); await recorder.setContentAndWait(` `, httpServer.PREFIX); + `, server.PREFIX); for (let i = 1; i < 3; ++i) await page.evaluate('pushState()'); - await page.goto(httpServer.PREFIX + '/page2.html'); - await recorder.waitForOutput('', `await page.goto('${httpServer.PREFIX}/page2.html');`); + await page.goto(server.PREFIX + '/page2.html'); + await recorder.waitForOutput('', `await page.goto('${server.PREFIX}/page2.html');`); }); test('should record slow navigation signal after mouse move', async ({ page, openRecorder, server }) => { diff --git a/tests/cli/cli-codegen-csharp.spec.ts b/tests/inspector/cli-codegen-csharp.spec.ts similarity index 98% rename from tests/cli/cli-codegen-csharp.spec.ts rename to tests/inspector/cli-codegen-csharp.spec.ts index e752de0df3..fbdbef867e 100644 --- a/tests/cli/cli-codegen-csharp.spec.ts +++ b/tests/inspector/cli-codegen-csharp.spec.ts @@ -16,7 +16,7 @@ import path from 'path'; import fs from 'fs'; -import { cliTest as test, expect } from '../config/cliTest'; +import { test, expect } from './inspectorTest'; const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString(); const launchOptions = (channel: string) => { diff --git a/tests/cli/cli-codegen-java.spec.ts b/tests/inspector/cli-codegen-java.spec.ts similarity index 98% rename from tests/cli/cli-codegen-java.spec.ts rename to tests/inspector/cli-codegen-java.spec.ts index 75d6cb35a1..2cbb29577a 100644 --- a/tests/cli/cli-codegen-java.spec.ts +++ b/tests/inspector/cli-codegen-java.spec.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import path from 'path'; -import { cliTest as test, expect } from '../config/cliTest'; +import { test, expect } from './inspectorTest'; const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString(); const launchOptions = (channel: string) => { diff --git a/tests/cli/cli-codegen-javascript.spec.ts b/tests/inspector/cli-codegen-javascript.spec.ts similarity index 98% rename from tests/cli/cli-codegen-javascript.spec.ts rename to tests/inspector/cli-codegen-javascript.spec.ts index d64ed99c02..acb9f1e8e8 100644 --- a/tests/cli/cli-codegen-javascript.spec.ts +++ b/tests/inspector/cli-codegen-javascript.spec.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import path from 'path'; -import { cliTest as test, expect } from '../config/cliTest'; +import { test, expect } from './inspectorTest'; const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString(); diff --git a/tests/cli/cli-codegen-python-async.spec.ts b/tests/inspector/cli-codegen-python-async.spec.ts similarity index 98% rename from tests/cli/cli-codegen-python-async.spec.ts rename to tests/inspector/cli-codegen-python-async.spec.ts index d17db779eb..a740130678 100644 --- a/tests/cli/cli-codegen-python-async.spec.ts +++ b/tests/inspector/cli-codegen-python-async.spec.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import path from 'path'; -import { cliTest as test, expect } from '../config/cliTest'; +import { test, expect } from './inspectorTest'; const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString(); const launchOptions = (channel: string) => { diff --git a/tests/cli/cli-codegen-python.spec.ts b/tests/inspector/cli-codegen-python.spec.ts similarity index 98% rename from tests/cli/cli-codegen-python.spec.ts rename to tests/inspector/cli-codegen-python.spec.ts index 0fbd53d99c..2ad7cb8b3b 100644 --- a/tests/cli/cli-codegen-python.spec.ts +++ b/tests/inspector/cli-codegen-python.spec.ts @@ -16,7 +16,7 @@ import fs from 'fs'; import path from 'path'; -import { cliTest as test, expect } from '../config/cliTest'; +import { test, expect } from './inspectorTest'; const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString(); const launchOptions = (channel: string) => { diff --git a/tests/config/cliTest.ts b/tests/inspector/inspectorTest.ts similarity index 89% rename from tests/config/cliTest.ts rename to tests/inspector/inspectorTest.ts index e9f6877839..33b9284226 100644 --- a/tests/config/cliTest.ts +++ b/tests/inspector/inspectorTest.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { contextTest } from './browserTest'; +import { contextTest } from '../config/browserTest'; import type { Page } from '../../index'; -import * as http from 'http'; import * as path from 'path'; import type { Source } from '../../src/server/supplements/recorder/recorderTypes'; import { ChildProcess, spawn } from 'child_process'; @@ -24,23 +23,15 @@ import { chromium } from '../../index'; import * as folio from 'folio'; export { expect } from 'folio'; -interface CLIHTTPServer { - setHandler: (handler: http.RequestListener) => void; - PREFIX: string; -} - type CLITestArgs = { - httpServer: CLIHTTPServer; recorderPageGetter: () => Promise; openRecorder: () => Promise; runCLI: (args: string[]) => CLIMock; }; -export const cliTest = contextTest.extend({ +export const test = contextTest.extend({ async beforeAll({}, workerInfo: folio.WorkerInfo) { - this._port = 10907 + workerInfo.workerIndex * 2; - this._server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => this._handler(req, res)).listen(this._port); - process.env.PWTEST_RECORDER_PORT = String(this._port + 1); + process.env.PWTEST_RECORDER_PORT = String(10907 + workerInfo.workerIndex); }, async beforeEach({ page, context, toImpl, browserName, browserChannel, headful, mode }, testInfo: folio.TestInfo): Promise { @@ -54,10 +45,6 @@ export const cliTest = contextTest.extend({ return c.pages()[0] || await c.waitForEvent('page'); }; return { - httpServer: { - setHandler: newHandler => this._handler = newHandler, - PREFIX: `http://127.0.0.1:${this._port}`, - }, runCLI: (cliArgs: string[]) => { this._cli = new CLIMock(browserName, browserChannel, !headful, cliArgs); return this._cli; @@ -76,13 +63,6 @@ export const cliTest = contextTest.extend({ this._cli = undefined; } }, - - async afterAll({}, workerInfo: folio.WorkerInfo) { - if (this._server) { - this._server.close(); - this._server = undefined; - } - }, }); class Recorder { diff --git a/tests/pause.spec.ts b/tests/inspector/pause.spec.ts similarity index 99% rename from tests/pause.spec.ts rename to tests/inspector/pause.spec.ts index df50e17766..9799c4dd5e 100644 --- a/tests/pause.spec.ts +++ b/tests/inspector/pause.spec.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { Page } from '../index'; -import { cliTest as it, expect } from './config/cliTest'; +import { Page } from '../../index'; +import { test as it, expect } from './inspectorTest'; it.describe('pause', () => { it.skip(({ mode }) => mode !== 'default');