From 97e4561ee16176410eab5472bce9afab01709eea Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 28 Aug 2020 13:53:47 -0700 Subject: [PATCH] feat(test): introduce metafunc for skip (#3676) --- test-runner/src/builtin.fixtures.ts | 23 +- test-runner/src/runner.ts | 1 + test-runner/src/spec.ts | 29 +- test-runner/src/test.ts | 68 +- test-runner/src/testRunner.ts | 5 +- test-runner/test/assets/allow-flaky.js | 4 +- test-runner/test/assets/expected-failure.js | 2 +- test-runner/test/assets/one-failure.js | 2 +- test-runner/test/assets/one-success.js | 2 +- test-runner/test/assets/unexpected-pass.js | 2 +- test/accessibility.spec.ts | 61 +- test/autowaiting-basic.spec.ts | 4 +- test/browsercontext-add-cookies.spec.ts | 4 +- test/browsercontext-cookies.spec.ts | 8 +- test/browsercontext-credentials.spec.ts | 8 +- test/browsercontext-device.spec.ts | 74 +- test/browsercontext-page-event.spec.ts | 12 +- test/browsercontext-viewport-mobile.spec.ts | 210 +++--- test/browsertype-basic.spec.ts | 4 +- test/browsertype-connect.spec.ts | 314 ++++---- test/browsertype-launch-server.spec.ts | 64 +- test/browsertype-launch.spec.ts | 25 +- test/capabilities.spec.ts | 8 +- test/channels.spec.ts | 4 +- test/chromium-css-coverage.spec.ts | 212 +++--- test/chromium-js-coverage.spec.ts | 155 ++-- test/chromium/chromium.spec.ts | 134 ++-- test/chromium/launcher.spec.ts | 20 +- test/chromium/oopif.spec.ts | 523 ++++++------- test/chromium/session.spec.ts | 202 ++--- test/chromium/tracing.spec.ts | 94 +-- test/click-react.spec.ts | 16 +- test/click-timeout-1.spec.ts | 4 +- test/click-timeout-3.spec.ts | 4 +- test/click.spec.ts | 8 +- test/defaultbrowsercontext-1.spec.ts | 5 +- test/defaultbrowsercontext-2.spec.ts | 41 +- test/dialog.spec.ts | 4 +- test/dispatchevent.spec.ts | 8 +- test/download.spec.ts | 16 +- test/electron/electron-app.spec.ts | 185 ++--- test/electron/electron-window.spec.ts | 54 +- test/elementhandle-bounding-box.spec.ts | 8 +- test/elementhandle-owner-frame.spec.ts | 4 +- test/elementhandle-screenshot.spec.ts | 706 +++++++++--------- ...ementhandle-wait-for-element-state.spec.ts | 6 +- test/emulation-focus.spec.ts | 4 +- test/firefox/launcher.spec.ts | 4 +- test/fixtures.spec.ts | 128 ++-- test/focus.spec.ts | 11 +- test/frame-evaluate.spec.ts | 16 +- test/frame-hierarchy.spec.ts | 4 +- test/headful.spec.ts | 14 +- test/keyboard.spec.ts | 12 +- test/mouse.spec.ts | 8 +- test/network-request.spec.ts | 4 +- test/page-add-script-tag.spec.ts | 4 +- test/page-basic.spec.ts | 4 +- test/page-evaluate.spec.ts | 4 +- test/page-event-crash.spec.ts | 73 +- test/page-event-pageerror.spec.ts | 12 +- test/page-fill.spec.ts | 12 +- test/page-goto.spec.ts | 5 +- test/page-history.spec.ts | 4 +- test/page-screenshot.spec.ts | 491 ++++++------ test/pdf.spec.ts | 9 +- test/permissions.spec.ts | 13 +- test/proxy.spec.ts | 5 +- test/request-fulfill.spec.ts | 9 +- test/screencast.spec.ts | 244 +++--- test/slowmo.spec.ts | 331 ++++---- test/snapshot.spec.ts | 4 +- 72 files changed, 2547 insertions(+), 2229 deletions(-) diff --git a/test-runner/src/builtin.fixtures.ts b/test-runner/src/builtin.fixtures.ts index 83d3d1c856..c7b6826f23 100644 --- a/test-runner/src/builtin.fixtures.ts +++ b/test-runner/src/builtin.fixtures.ts @@ -20,32 +20,29 @@ import { promisify } from 'util'; import fs from 'fs'; import rimraf from 'rimraf'; import { registerFixture } from './fixtures'; +import { Test } from './test'; +interface Describers { + it(name: string, inner: (state: STATE) => Promise | void): void; + it(name: string, modifier: (test: Test) => any, inner: (state: STATE) => Promise | void): void; +} declare global { type DescribeFunction = ((name: string, inner: () => void) => void) & { fail(condition: boolean): DescribeFunction; skip(condition: boolean): DescribeFunction; + fixme(condition: boolean): DescribeFunction; + flaky(condition: boolean): DescribeFunction; slow(): DescribeFunction; repeat(n: number): DescribeFunction; }; - type ItFunction = ((name: string, inner: (state: STATE) => Promise | void) => void) & { - fail(condition: boolean): ItFunction; - fixme(condition: boolean): ItFunction; - flaky(condition: boolean): ItFunction; - skip(condition: boolean): ItFunction; - slow(): ItFunction; - repeat(n: number): ItFunction; - }; - const describe: DescribeFunction; const fdescribe: DescribeFunction; const xdescribe: DescribeFunction; - const it: ItFunction; - const fit: ItFunction; - const dit: ItFunction; - const xit: ItFunction; + const it: Describers['it']; + const fit: Describers['it']; + const xit: Describers['it']; const beforeEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise) => void; const afterEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise) => void; diff --git a/test-runner/src/runner.ts b/test-runner/src/runner.ts index cdd2093af9..decf4f6290 100644 --- a/test-runner/src/runner.ts +++ b/test-runner/src/runner.ts @@ -176,6 +176,7 @@ export class Runner { const { test } = this._testById.get(params.id); test._skipped = params.skipped; test._flaky = params.flaky; + test._slow = params.slow; test._expectedStatus = params.expectedStatus; this._reporter.onTestBegin(test); }); diff --git a/test-runner/src/spec.ts b/test-runner/src/spec.ts index f9a70a29ac..a52e1121a0 100644 --- a/test-runner/src/spec.ts +++ b/test-runner/src/spec.ts @@ -53,32 +53,31 @@ export function spec(suite: Suite, file: string, timeout: number): () => void { const suites = [suite]; suite.file = file; - const it = specBuilder(['skip', 'fixme', 'fail', 'slow', 'only', 'flaky'], (specs, title, fn) => { + const it = specBuilder(['_skip', '_only'], (specs: any, title: string, metaFn: (test: Test) => void | Function, fn?: Function) => { const suite = suites[0]; + if (typeof fn !== 'function') { + fn = metaFn; + metaFn = null; + } const test = new Test(title, fn); + if (metaFn) + metaFn(test); test.file = file; - test.slow = specs.slow && specs.slow[0]; test.timeout = timeout; - - const only = specs.only && specs.only[0]; + const only = specs._only && specs._only[0]; if (only) test.only = true; - if (!only && specs.skip && specs.skip[0]) + if (!only && specs._skip && specs._skip[0]) test._skipped = true; - if (!only && specs.fixme && specs.fixme[0]) - test._skipped = true; - if (specs.fail && specs.fail[0]) - test._expectedStatus = 'failed'; - if (specs.flaky && specs.flaky[0]) - test._flaky = true; suite._addTest(test); return test; }); - const describe = specBuilder(['skip', 'fixme', 'only'], (specs, title, fn) => { + const describe = specBuilder(['skip', 'fixme', 'flaky', 'only', 'slow'], (specs, title, fn) => { const child = new Suite(title, suites[0]); suites[0]._addSuite(child); child.file = file; + child._slow = specs.slow && specs.slow[0]; const only = specs.only && specs.only[0]; if (only) child.only = true; @@ -86,6 +85,8 @@ export function spec(suite: Suite, file: string, timeout: number): () => void { child._skipped = true; if (!only && specs.fixme && specs.fixme[0]) child._skipped = true; + if (specs.flaky && specs.flaky[0]) + child._flaky = true; suites.unshift(child); fn(); suites.shift(); @@ -99,8 +100,8 @@ export function spec(suite: Suite, file: string, timeout: number): () => void { (global as any).fdescribe = describe.only(true); (global as any).xdescribe = describe.skip(true); (global as any).it = it; - (global as any).fit = it.only(true); - (global as any).xit = it.skip(true); + (global as any).fit = it._only(true); + (global as any).xit = it._skip(true); return installTransform(); } diff --git a/test-runner/src/test.ts b/test-runner/src/test.ts index 4a681dcef1..9a1a0d3096 100644 --- a/test-runner/src/test.ts +++ b/test-runner/src/test.ts @@ -23,7 +23,6 @@ export class Test { title: string; file: string; only = false; - slow = false; timeout = 0; fn: Function; results: TestResult[] = []; @@ -33,9 +32,11 @@ export class Test { // We will compute them there and send to the runner (front-end) _skipped = false; _flaky = false; + _slow = false; + _expectedStatus: TestStatus = 'passed'; + _overriddenFn: Function; _startTime: number; - _expectedStatus: TestStatus = 'passed'; constructor(title: string, fn: Function) { this.title = title; @@ -50,6 +51,56 @@ export class Test { return this.titlePath().join(' '); } + slow(): void; + slow(condition: boolean): void; + slow(description: string): void; + slow(condition: boolean, description: string): void; + slow(arg?: boolean | string, description?: string) { + const condition = typeof arg === 'boolean' ? arg : true; + if (condition) + this._slow = true; + } + + skip(): void; + skip(condition: boolean): void; + skip(description: string): void; + skip(condition: boolean, description: string): void; + skip(arg?: boolean | string, description?: string) { + const condition = typeof arg === 'boolean' ? arg : true; + if (condition) + this._skipped = true; + } + + fixme(): void; + fixme(condition: boolean): void; + fixme(description: string): void; + fixme(condition: boolean, description: string): void; + fixme(arg?: boolean | string, description?: string) { + const condition = typeof arg === 'boolean' ? arg : true; + if (condition) + this._skipped = true; + } + + flaky(): void; + flaky(condition: boolean): void; + flaky(description: string): void; + flaky(condition: boolean, description: string): void; + flaky(arg?: boolean | string, description?: string) { + const condition = typeof arg === 'boolean' ? arg : true; + if (condition) + this._flaky = true; + } + + fail(): void; + fail(condition: boolean): void; + fail(description: string): void; + fail(condition: boolean, description: string): void; + fail(arg?: boolean | string, description?: string) { + const condition = typeof arg === 'boolean' ? arg : true; + if (condition) + this._expectedStatus = 'failed'; + } + _appendResult(): TestResult { const result: TestResult = { duration: 0, @@ -85,6 +136,7 @@ export class Test { test.file = this.file; test.timeout = this.timeout; test._flaky = this._flaky; + test._slow = this._slow; test._overriddenFn = this._overriddenFn; return test; } @@ -107,6 +159,8 @@ export class Suite { tests: Test[] = []; only = false; file: string; + _flaky = false; + _slow = false; configuration: Configuration; // Skipped & flaky are resolved based on options in worker only @@ -140,6 +194,14 @@ export class Suite { return this._skipped || (this.parent && this.parent._isSkipped()); } + _isSlow(): boolean { + return this._slow || (this.parent && this.parent._isSlow()); + } + + _isFlaky(): boolean { + return this._flaky || (this.parent && this.parent._isFlaky()); + } + _addTest(test: Test) { test.suite = this; this.tests.push(test); @@ -176,7 +238,9 @@ export class Suite { const suite = new Suite(this.title); suite.only = this.only; suite.file = this.file; + suite._flaky = this._flaky; suite._skipped = this._skipped; + suite._slow = this._slow; return suite; } diff --git a/test-runner/src/testRunner.ts b/test-runner/src/testRunner.ts index d5d979190c..6aa3bcdb3d 100644 --- a/test-runner/src/testRunner.ts +++ b/test-runner/src/testRunner.ts @@ -155,10 +155,13 @@ export class TestRunner extends EventEmitter { // We only know resolved skipped/flaky value in the worker, // send it to the runner. test._skipped = test._skipped || test.suite._isSkipped(); + test._flaky = test._flaky || test.suite._isFlaky(); + test._slow = test._slow || test.suite._isSlow(); this.emit('testBegin', { id, skipped: test._skipped, flaky: test._flaky, + slow: test._slow }); const result: TestResult = { @@ -182,7 +185,7 @@ export class TestRunner extends EventEmitter { const testInfo = { config: this._config, test, result }; if (!this._trialRun) { await this._runHooks(test.suite, 'beforeEach', 'before', testInfo); - const timeout = test.slow ? this._timeout * 3 : this._timeout; + const timeout = test._slow || test.suite._isSlow() ? this._timeout * 3 : this._timeout; await fixturePool.runTestWithFixtures(test.fn, timeout, testInfo); await this._runHooks(test.suite, 'afterEach', 'after', testInfo); } else { diff --git a/test-runner/test/assets/allow-flaky.js b/test-runner/test/assets/allow-flaky.js index 4c017e8c15..78677d718c 100644 --- a/test-runner/test/assets/allow-flaky.js +++ b/test-runner/test/assets/allow-flaky.js @@ -17,7 +17,9 @@ const fs = require('fs'); const path = require('path'); -it.flaky('flake', async ({}) => { +it('flake', test => { + test.flaky(); +}, async ({}) => { try { fs.readFileSync(path.join(__dirname, '..', 'test-results', 'allow-flaky.txt')); } catch (e) { diff --git a/test-runner/test/assets/expected-failure.js b/test-runner/test/assets/expected-failure.js index 6d9555f11e..b265756e75 100644 --- a/test-runner/test/assets/expected-failure.js +++ b/test-runner/test/assets/expected-failure.js @@ -15,7 +15,7 @@ */ require('../../lib'); -it.fail(true)('fails',() => { +it('fails', test => test.fail(), () => { expect(1 + 1).toBe(3); }); diff --git a/test-runner/test/assets/one-failure.js b/test-runner/test/assets/one-failure.js index a74fb8e15d..2261932954 100644 --- a/test-runner/test/assets/one-failure.js +++ b/test-runner/test/assets/one-failure.js @@ -15,6 +15,6 @@ */ require('../../'); -it('fails',() => { +it('fails', () => { expect(1 + 1).toBe(7); }); diff --git a/test-runner/test/assets/one-success.js b/test-runner/test/assets/one-success.js index e3fec57a76..e2b85cdee3 100644 --- a/test-runner/test/assets/one-success.js +++ b/test-runner/test/assets/one-success.js @@ -15,6 +15,6 @@ */ require('../../'); -it('succeeds',() => { +it('succeeds', () => { expect(1 + 1).toBe(2); }); diff --git a/test-runner/test/assets/unexpected-pass.js b/test-runner/test/assets/unexpected-pass.js index 114e650b1d..0aaf70c228 100644 --- a/test-runner/test/assets/unexpected-pass.js +++ b/test-runner/test/assets/unexpected-pass.js @@ -15,6 +15,6 @@ */ require('../../'); -it.fail(true)('succeeds',() => { +it('succeeds', test => test.fail(), () => { expect(1 + 1).toBe(2); }); diff --git a/test/accessibility.spec.ts b/test/accessibility.spec.ts index 57078ab52b..c3797fba48 100644 --- a/test/accessibility.spec.ts +++ b/test/accessibility.spec.ts @@ -138,8 +138,9 @@ it('should not report text nodes inside controls', async function({page}) { expect(await page.accessibility.snapshot()).toEqual(golden); }); -// WebKit rich text accessibility is iffy -it.skip(options.WEBKIT)('rich text editable fields should have children', async function({page}) { +it('rich text editable fields should have children', test => { + test.skip(options.WEBKIT, 'WebKit rich text accessibility is iffy'); +}, async function({page}) { await page.setContent(`
Edit this image: my fake image @@ -169,8 +170,10 @@ it.skip(options.WEBKIT)('rich text editable fields should have children', async const snapshot = await page.accessibility.snapshot(); expect(snapshot.children[0]).toEqual(golden); }); -// WebKit rich text accessibility is iffy -it.skip(options.WEBKIT)('rich text editable fields with role should have children', async function({page}) { + +it('rich text editable fields with role should have children', test => { + test.skip(options.WEBKIT, 'WebKit rich text accessibility is iffy'); +}, async function({page}) { await page.setContent(`
Edit this image: my fake image @@ -199,36 +202,38 @@ it.skip(options.WEBKIT)('rich text editable fields with role should have childre expect(snapshot.children[0]).toEqual(golden); }); -it.skip(options.FIREFOX || options.WEBKIT)('plain text field with role should not have children', async function({page}) { +describe.skip(options.FIREFOX || options.WEBKIT)('contenteditable', () => { // Firefox does not support contenteditable="plaintext-only". // WebKit rich text accessibility is iffy - await page.setContent(` -
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'textbox', - name: '', - value: 'Edit this image:' + it('plain text field with role should not have children', async function({page}) { + await page.setContent(` +
Edit this image:my fake image
`); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual({ + role: 'textbox', + name: '', + value: 'Edit this image:' + }); }); -}); -it.skip(options.FIREFOX || options.WEBKIT)('plain text field without role should not have content', async function({page}) { - await page.setContent(` -
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'generic', - name: '' + it('plain text field without role should not have content', async function({page}) { + await page.setContent(` +
Edit this image:my fake image
`); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual({ + role: 'generic', + name: '' + }); }); -}); -it.skip(options.FIREFOX || options.WEBKIT)('plain text field with tabindex and without role should not have content', async function({page}) { - await page.setContent(` -
Edit this image:my fake image
`); - const snapshot = await page.accessibility.snapshot(); - expect(snapshot.children[0]).toEqual({ - role: 'generic', - name: '' + it('plain text field with tabindex and without role should not have content', async function({page}) { + await page.setContent(` +
Edit this image:my fake image
`); + const snapshot = await page.accessibility.snapshot(); + expect(snapshot.children[0]).toEqual({ + role: 'generic', + name: '' + }); }); }); diff --git a/test/autowaiting-basic.spec.ts b/test/autowaiting-basic.spec.ts index b8eefe371e..3b5ba095d3 100644 --- a/test/autowaiting-basic.spec.ts +++ b/test/autowaiting-basic.spec.ts @@ -200,7 +200,9 @@ it('should work with goto following click', async ({page, server}) => { await page.goto(server.EMPTY_PAGE); }); -it.skip(options.WIRE)('should report navigation in the log when clicking anchor', async ({page, server}) => { +it('should report navigation in the log when clicking anchor', test => { + test.skip(options.WIRE); +}, async ({page, server}) => { await page.setContent(`click me`); const __testHookAfterPointerAction = () => new Promise(f => setTimeout(f, 6000)); const error = await page.click('a', { timeout: 5000, __testHookAfterPointerAction } as any).catch(e => e); diff --git a/test/browsercontext-add-cookies.spec.ts b/test/browsercontext-add-cookies.spec.ts index 990f121099..e9da00c1a6 100644 --- a/test/browsercontext-add-cookies.spec.ts +++ b/test/browsercontext-add-cookies.spec.ts @@ -138,7 +138,9 @@ it('should isolate send cookie header', async ({server, context, browser}) => { } }); -it.slow()('should isolate cookies between launches', async ({browserType, server, defaultBrowserOptions}) => { +it('should isolate cookies between launches', test => { + test.slow(); +}, async ({browserType, server, defaultBrowserOptions}) => { const browser1 = await browserType.launch(defaultBrowserOptions); const context1 = await browser1.newContext(); await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]); diff --git a/test/browsercontext-cookies.spec.ts b/test/browsercontext-cookies.spec.ts index 7c01d72645..efa5369e6b 100644 --- a/test/browsercontext-cookies.spec.ts +++ b/test/browsercontext-cookies.spec.ts @@ -72,7 +72,9 @@ it('should properly report httpOnly cookie', async ({context, page, server}) => expect(cookies[0].httpOnly).toBe(true); }); -it.fail(options.WEBKIT && WIN)('should properly report "Strict" sameSite cookie', async ({context, page, server}) => { +it('should properly report "Strict" sameSite cookie', test => { + test.fail(options.WEBKIT && WIN); +}, async ({context, page, server}) => { server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', 'name=value;SameSite=Strict'); res.end(); @@ -83,7 +85,9 @@ it.fail(options.WEBKIT && WIN)('should properly report "Strict" sameSite cookie' expect(cookies[0].sameSite).toBe('Strict'); }); -it.fail(options.WEBKIT && WIN)('should properly report "Lax" sameSite cookie', async ({context, page, server}) => { +it('should properly report "Lax" sameSite cookie', test => { + test.fail(options.WEBKIT && WIN); +}, async ({context, page, server}) => { server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', 'name=value;SameSite=Lax'); res.end(); diff --git a/test/browsercontext-credentials.spec.ts b/test/browsercontext-credentials.spec.ts index 2ea2013caf..57e711bb77 100644 --- a/test/browsercontext-credentials.spec.ts +++ b/test/browsercontext-credentials.spec.ts @@ -16,7 +16,9 @@ */ import { options } from './playwright.fixtures'; -it.fail(options.CHROMIUM && !options.HEADLESS)('should fail without credentials', async ({browser, server}) => { +it('should fail without credentials', test => { + test.fail(options.CHROMIUM && !options.HEADLESS); +}, async ({browser, server}) => { server.setAuth('/empty.html', 'user', 'pass'); const context = await browser.newContext(); const page = await context.newPage(); @@ -25,7 +27,9 @@ it.fail(options.CHROMIUM && !options.HEADLESS)('should fail without credentials' await context.close(); }); -it.fail(options.CHROMIUM && !options.HEADLESS)('should work with setHTTPCredentials', async ({browser, server}) => { +it('should work with setHTTPCredentials', test => { + test.fail(options.CHROMIUM && !options.HEADLESS); +}, async ({browser, server}) => { server.setAuth('/empty.html', 'user', 'pass'); const context = await browser.newContext(); const page = await context.newPage(); diff --git a/test/browsercontext-device.spec.ts b/test/browsercontext-device.spec.ts index 63c4073951..8fb7cf5832 100644 --- a/test/browsercontext-device.spec.ts +++ b/test/browsercontext-device.spec.ts @@ -16,41 +16,43 @@ */ import { options } from './playwright.fixtures'; -it.skip(options.FIREFOX)('should work', async ({playwright, browser, server}) => { - const iPhone = playwright.devices['iPhone 6']; - const context = await browser.newContext({ ...iPhone }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => window.innerWidth)).toBe(375); - expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone'); - await context.close(); -}); - -it.skip(options.FIREFOX)('should support clicking', async ({playwright, browser, server}) => { - const iPhone = playwright.devices['iPhone 6']; - const context = await browser.newContext({ ...iPhone }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/input/button.html'); - const button = await page.$('button'); - await page.evaluate(button => button.style.marginTop = '200px', button); - await button.click(); - expect(await page.evaluate('result')).toBe('Clicked'); - await context.close(); -}); - -it.skip(options.FIREFOX)('should scroll to click', async ({browser, server}) => { - const context = await browser.newContext({ - viewport: { - width: 400, - height: 400, - }, - deviceScaleFactor: 1, - isMobile: true +describe.skip(options.FIREFOX)('device', () => { + it('should work', async ({playwright, browser, server}) => { + const iPhone = playwright.devices['iPhone 6']; + const context = await browser.newContext({ ...iPhone }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + expect(await page.evaluate(() => window.innerWidth)).toBe(375); + expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone'); + await context.close(); + }); + + it('should support clicking', async ({playwright, browser, server}) => { + const iPhone = playwright.devices['iPhone 6']; + const context = await browser.newContext({ ...iPhone }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/input/button.html'); + const button = await page.$('button'); + await page.evaluate(button => button.style.marginTop = '200px', button); + await button.click(); + expect(await page.evaluate('result')).toBe('Clicked'); + await context.close(); + }); + + it('should scroll to click', async ({browser, server}) => { + const context = await browser.newContext({ + viewport: { + width: 400, + height: 400, + }, + deviceScaleFactor: 1, + isMobile: true + }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/input/scrollable.html'); + const element = await page.$('#button-91'); + await element.click(); + expect(await element.textContent()).toBe('clicked'); + await context.close(); }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/input/scrollable.html'); - const element = await page.$('#button-91'); - await element.click(); - expect(await element.textContent()).toBe('clicked'); - await context.close(); }); diff --git a/test/browsercontext-page-event.spec.ts b/test/browsercontext-page-event.spec.ts index d64f3b06ed..d2a1062238 100644 --- a/test/browsercontext-page-event.spec.ts +++ b/test/browsercontext-page-event.spec.ts @@ -156,8 +156,9 @@ it('should fire page lifecycle events', async function({browser, server}) { await context.close(); }); -it.fixme(options.WEBKIT)('should work with Shift-clicking', async ({browser, server}) => { - // WebKit: Shift+Click does not open a new window. +it('should work with Shift-clicking', test => { + test.fixme(options.WEBKIT, 'WebKit: Shift+Click does not open a new window.'); +}, async ({browser, server}) => { const context = await browser.newContext(); const page = await context.newPage(); await page.goto(server.EMPTY_PAGE); @@ -170,9 +171,10 @@ it.fixme(options.WEBKIT)('should work with Shift-clicking', async ({browser, ser await context.close(); }); -it.fixme(options.WEBKIT || options.FIREFOX)('should work with Ctrl-clicking', async ({browser, server}) => { - // Firefox: reports an opener in this case. - // WebKit: Ctrl+Click does not open a new tab. +it('should work with Ctrl-clicking', test => { + test.fixme(options.WEBKIT, 'Ctrl+Click does not open a new tab.'); + test.fixme(options.FIREFOX, 'Reports an opener in this case.'); +}, async ({browser, server}) => { const context = await browser.newContext(); const page = await context.newPage(); await page.goto(server.EMPTY_PAGE); diff --git a/test/browsercontext-viewport-mobile.spec.ts b/test/browsercontext-viewport-mobile.spec.ts index dd14e29b25..946604f8eb 100644 --- a/test/browsercontext-viewport-mobile.spec.ts +++ b/test/browsercontext-viewport-mobile.spec.ts @@ -17,113 +17,115 @@ import { options } from './playwright.fixtures'; -it.skip(options.FIREFOX)('should support mobile emulation', async ({playwright, browser, server}) => { - const iPhone = playwright.devices['iPhone 6']; - const context = await browser.newContext({ ...iPhone }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => window.innerWidth)).toBe(375); - await page.setViewportSize({width: 400, height: 300}); - expect(await page.evaluate(() => window.innerWidth)).toBe(400); - await context.close(); -}); - -it.skip(options.FIREFOX)('should support touch emulation', async ({playwright, browser, server}) => { - const iPhone = playwright.devices['iPhone 6']; - const context = await browser.newContext({ ...iPhone }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); - expect(await page.evaluate(dispatchTouch)).toBe('Received touch'); - await context.close(); - - function dispatchTouch() { - let fulfill; - const promise = new Promise(x => fulfill = x); - window.ontouchstart = function(e) { - fulfill('Received touch'); - }; - window.dispatchEvent(new Event('touchstart')); - - fulfill('Did not receive touch'); - - return promise; - } -}); - -it.skip(options.FIREFOX)('should be detectable by Modernizr', async ({playwright, browser, server}) => { - const iPhone = playwright.devices['iPhone 6']; - const context = await browser.newContext({ ...iPhone }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/detect-touch.html'); - expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); - await context.close(); -}); - -it.skip(options.FIREFOX)('should detect touch when applying viewport with touches', async ({browser, server}) => { - const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true }); - const page = await context.newPage(); - await page.goto(server.EMPTY_PAGE); - await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); - expect(await page.evaluate(() => window['Modernizr'].touchevents)).toBe(true); - await context.close(); -}); - -it.skip(options.FIREFOX)('should support landscape emulation', async ({playwright, browser, server}) => { - const iPhone = playwright.devices['iPhone 6']; - const iPhoneLandscape = playwright.devices['iPhone 6 landscape']; - const context1 = await browser.newContext({ ...iPhone }); - const page1 = await context1.newPage(); - await page1.goto(server.PREFIX + '/mobile.html'); - expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false); - const context2 = await browser.newContext({ ...iPhoneLandscape }); - const page2 = await context2.newPage(); - expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true); - await context1.close(); - await context2.close(); -}); - -it.skip(options.FIREFOX)('should support window.orientation emulation', async ({browser, server}) => { - const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => window.orientation)).toBe(0); - await page.setViewportSize({width: 400, height: 300}); - expect(await page.evaluate(() => window.orientation)).toBe(90); - await context.close(); -}); - -it.skip(options.FIREFOX)('should fire orientationchange event', async ({browser, server}) => { - const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/mobile.html'); - await page.evaluate(() => { - let counter = 0; - window.addEventListener('orientationchange', () => console.log(++counter)); +describe.skip(options.FIREFOX)('mobile viewport', () => { + it('should support mobile emulation', async ({playwright, browser, server}) => { + const iPhone = playwright.devices['iPhone 6']; + const context = await browser.newContext({ ...iPhone }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + expect(await page.evaluate(() => window.innerWidth)).toBe(375); + await page.setViewportSize({width: 400, height: 300}); + expect(await page.evaluate(() => window.innerWidth)).toBe(400); + await context.close(); }); - const event1 = page.waitForEvent('console'); - await page.setViewportSize({width: 400, height: 300}); - expect((await event1).text()).toBe('1'); + it('should support touch emulation', async ({playwright, browser, server}) => { + const iPhone = playwright.devices['iPhone 6']; + const context = await browser.newContext({ ...iPhone }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); + expect(await page.evaluate(dispatchTouch)).toBe('Received touch'); + await context.close(); - const event2 = page.waitForEvent('console'); - await page.setViewportSize({width: 300, height: 400}); - expect((await event2).text()).toBe('2'); - await context.close(); -}); + function dispatchTouch() { + let fulfill; + const promise = new Promise(x => fulfill = x); + window.ontouchstart = function(e) { + fulfill('Received touch'); + }; + window.dispatchEvent(new Event('touchstart')); -it.skip(options.FIREFOX)('default mobile viewports to 980 width', async ({browser, server}) => { - const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/empty.html'); - expect(await page.evaluate(() => window.innerWidth)).toBe(980); - await context.close(); -}); + fulfill('Did not receive touch'); -it.skip(options.FIREFOX)('respect meta viewport tag', async ({browser, server}) => { - const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/mobile.html'); - expect(await page.evaluate(() => window.innerWidth)).toBe(320); - await context.close(); + return promise; + } + }); + + it('should be detectable by Modernizr', async ({playwright, browser, server}) => { + const iPhone = playwright.devices['iPhone 6']; + const context = await browser.newContext({ ...iPhone }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/detect-touch.html'); + expect(await page.evaluate(() => document.body.textContent.trim())).toBe('YES'); + await context.close(); + }); + + it('should detect touch when applying viewport with touches', async ({browser, server}) => { + const context = await browser.newContext({ viewport: { width: 800, height: 600 }, hasTouch: true }); + const page = await context.newPage(); + await page.goto(server.EMPTY_PAGE); + await page.addScriptTag({url: server.PREFIX + '/modernizr.js'}); + expect(await page.evaluate(() => window['Modernizr'].touchevents)).toBe(true); + await context.close(); + }); + + it('should support landscape emulation', async ({playwright, browser, server}) => { + const iPhone = playwright.devices['iPhone 6']; + const iPhoneLandscape = playwright.devices['iPhone 6 landscape']; + const context1 = await browser.newContext({ ...iPhone }); + const page1 = await context1.newPage(); + await page1.goto(server.PREFIX + '/mobile.html'); + expect(await page1.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(false); + const context2 = await browser.newContext({ ...iPhoneLandscape }); + const page2 = await context2.newPage(); + expect(await page2.evaluate(() => matchMedia('(orientation: landscape)').matches)).toBe(true); + await context1.close(); + await context2.close(); + }); + + it('should support window.orientation emulation', async ({browser, server}) => { + const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + expect(await page.evaluate(() => window.orientation)).toBe(0); + await page.setViewportSize({width: 400, height: 300}); + expect(await page.evaluate(() => window.orientation)).toBe(90); + await context.close(); + }); + + it('should fire orientationchange event', async ({browser, server}) => { + const context = await browser.newContext({ viewport: { width: 300, height: 400 }, isMobile: true }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + await page.evaluate(() => { + let counter = 0; + window.addEventListener('orientationchange', () => console.log(++counter)); + }); + + const event1 = page.waitForEvent('console'); + await page.setViewportSize({width: 400, height: 300}); + expect((await event1).text()).toBe('1'); + + const event2 = page.waitForEvent('console'); + await page.setViewportSize({width: 300, height: 400}); + expect((await event2).text()).toBe('2'); + await context.close(); + }); + + it('default mobile viewports to 980 width', async ({browser, server}) => { + const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/empty.html'); + expect(await page.evaluate(() => window.innerWidth)).toBe(980); + await context.close(); + }); + + it('respect meta viewport tag', async ({browser, server}) => { + const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/mobile.html'); + expect(await page.evaluate(() => window.innerWidth)).toBe(320); + await context.close(); + }); }); diff --git a/test/browsertype-basic.spec.ts b/test/browsertype-basic.spec.ts index 258b5e8e05..85c8cb8bfc 100644 --- a/test/browsertype-basic.spec.ts +++ b/test/browsertype-basic.spec.ts @@ -18,7 +18,9 @@ import fs from 'fs'; import { options } from './playwright.fixtures'; -it.skip(Boolean(process.env.CRPATH || process.env.FFPATH || process.env.WKPATH))('browserType.executablePath should work', async ({browserType}) => { +it('browserType.executablePath should work', test => { + test.skip(Boolean(process.env.CRPATH || process.env.FFPATH || process.env.WKPATH)); +}, async ({browserType}) => { const executablePath = browserType.executablePath(); expect(fs.existsSync(executablePath)).toBe(true); expect(fs.realpathSync(executablePath)).toBe(executablePath); diff --git a/test/browsertype-connect.spec.ts b/test/browsertype-connect.spec.ts index bc4a8b0783..b3d9b42de5 100644 --- a/test/browsertype-connect.spec.ts +++ b/test/browsertype-connect.spec.ts @@ -19,162 +19,166 @@ import { options } from './playwright.fixtures'; import utils from './utils'; import './remoteServer.fixture'; -it.skip(options.WIRE).slow()('should be able to reconnect to a browser', async ({browserType, remoteServer, server}) => { - { - const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const browserContext = await browser.newContext(); - expect(browserContext.pages().length).toBe(0); - const page = await browserContext.newPage(); - expect(await page.evaluate('11 * 11')).toBe(121); - await page.goto(server.EMPTY_PAGE); - await browser.close(); - } - { - const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const browserContext = await browser.newContext(); - const page = await browserContext.newPage(); - await page.goto(server.EMPTY_PAGE); - await browser.close(); - } -}); - -it.skip(options.WIRE).slow()('should be able to connect two browsers at the same time', async ({browserType, remoteServer}) => { - const browser1 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - expect(browser1.contexts().length).toBe(0); - await browser1.newContext(); - expect(browser1.contexts().length).toBe(1); - - const browser2 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - expect(browser2.contexts().length).toBe(0); - await browser2.newContext(); - expect(browser2.contexts().length).toBe(1); - expect(browser1.contexts().length).toBe(1); - - await browser1.close(); - const page2 = await browser2.newPage(); - expect(await page2.evaluate(() => 7 * 6)).toBe(42); // original browser should still work - - await browser2.close(); -}); - -it.skip(options.WIRE).slow()('disconnected event should be emitted when browser is closed or server is closed', async ({browserType, remoteServer}) => { - const browser1 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const browser2 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - - let disconnected1 = 0; - let disconnected2 = 0; - browser1.on('disconnected', () => ++disconnected1); - browser2.on('disconnected', () => ++disconnected2); - - await Promise.all([ - new Promise(f => browser1.on('disconnected', f)), - browser1.close(), - ]); - expect(disconnected1).toBe(1); - expect(disconnected2).toBe(0); - - await Promise.all([ - new Promise(f => browser2.on('disconnected', f)), - remoteServer.close(), - ]); - expect(disconnected1).toBe(1); - expect(disconnected2).toBe(1); -}); - -it.skip(options.WIRE).slow()('should handle exceptions during connect', async ({browserType, remoteServer}) => { - const __testHookBeforeCreateBrowser = () => { throw new Error('Dummy'); }; - const error = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint(), __testHookBeforeCreateBrowser } as any).catch(e => e); - expect(error.message).toContain('Dummy'); -}); - -it.skip(options.WIRE).slow()('should set the browser connected state', async ({browserType, remoteServer}) => { - const remote = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - expect(remote.isConnected()).toBe(true); - await remote.close(); - expect(remote.isConnected()).toBe(false); -}); - -it.skip(options.WIRE).slow()('should throw when used after isConnected returns false', async ({browserType, remoteServer}) => { - const remote = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const page = await remote.newPage(); - await Promise.all([ - remoteServer.close(), - new Promise(f => remote.once('disconnected', f)), - ]); - expect(remote.isConnected()).toBe(false); - const error = await page.evaluate('1 + 1').catch(e => e) as Error; - expect(error.message).toContain('has been closed'); -}); - -it.skip(options.WIRE).slow()('should reject navigation when browser closes', async ({browserType, remoteServer, server}) => { - server.setRoute('/one-style.css', () => {}); - const remote = await browserType.connect({ wsEndpoint: remoteServer.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!'); -}); - -it.skip(options.WIRE).slow()('should reject waitForSelector when browser closes', async ({browserType, remoteServer, server}) => { - server.setRoute('/empty.html', () => {}); - const remote = await browserType.connect({ wsEndpoint: remoteServer.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'); -}); - -it.skip(options.WIRE).slow()('should emit close events on pages and contexts', async ({browserType, remoteServer}) => { - const remote = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const context = await remote.newContext(); - const page = await context.newPage(); - let pageClosed = false; - page.on('close', () => pageClosed = true); - await Promise.all([ - new Promise(f => context.on('close', f)), - remoteServer.close() - ]); - expect(pageClosed).toBeTruthy(); -}); - -it.skip(options.WIRE).slow()('should terminate network waiters', async ({browserType, remoteServer, server}) => { - const remote = await browserType.connect({ wsEndpoint: remoteServer.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), - remoteServer.close(), - ]); - for (let i = 0; i < 2; i++) { - const message = results[i].message; - expect(message).toContain('Page closed'); - expect(message).not.toContain('Timeout'); - } -}); - -it.skip(options.WIRE).fail(true).slow()('should respect selectors', async ({ playwright, browserType, remoteServer }) => { - const mycss = () => ({ - create(root, target) {}, - query(root, selector) { - return root.querySelector(selector); - }, - queryAll(root: HTMLElement, selector: string) { - return Array.from(root.querySelectorAll(selector)); +describe.skip(options.WIRE).slow()('connect', () => { + it('should be able to reconnect to a browser', async ({browserType, remoteServer, server}) => { + { + const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const browserContext = await browser.newContext(); + expect(browserContext.pages().length).toBe(0); + const page = await browserContext.newPage(); + expect(await page.evaluate('11 * 11')).toBe(121); + await page.goto(server.EMPTY_PAGE); + await browser.close(); + } + { + const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const browserContext = await browser.newContext(); + const page = await browserContext.newPage(); + await page.goto(server.EMPTY_PAGE); + await browser.close(); } }); - await utils.registerEngine(playwright, 'mycss', mycss); - const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); - const page = await browser.newPage(); - await page.setContent(`
hello
`); - expect(await page.innerHTML('css=div')).toBe('hello'); - expect(await page.innerHTML('mycss=div')).toBe('hello'); - await browser.close(); + it('should be able to connect two browsers at the same time', async ({browserType, remoteServer}) => { + const browser1 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + expect(browser1.contexts().length).toBe(0); + await browser1.newContext(); + expect(browser1.contexts().length).toBe(1); + + const browser2 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + expect(browser2.contexts().length).toBe(0); + await browser2.newContext(); + expect(browser2.contexts().length).toBe(1); + expect(browser1.contexts().length).toBe(1); + + await browser1.close(); + const page2 = await browser2.newPage(); + expect(await page2.evaluate(() => 7 * 6)).toBe(42); // original browser should still work + + await browser2.close(); + }); + + it('disconnected event should be emitted when browser is closed or server is closed', async ({browserType, remoteServer}) => { + const browser1 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const browser2 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + + let disconnected1 = 0; + let disconnected2 = 0; + browser1.on('disconnected', () => ++disconnected1); + browser2.on('disconnected', () => ++disconnected2); + + await Promise.all([ + new Promise(f => browser1.on('disconnected', f)), + browser1.close(), + ]); + expect(disconnected1).toBe(1); + expect(disconnected2).toBe(0); + + await Promise.all([ + new Promise(f => browser2.on('disconnected', f)), + remoteServer.close(), + ]); + expect(disconnected1).toBe(1); + expect(disconnected2).toBe(1); + }); + + it('should handle exceptions during connect', async ({browserType, remoteServer}) => { + const __testHookBeforeCreateBrowser = () => { throw new Error('Dummy'); }; + const error = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint(), __testHookBeforeCreateBrowser } as any).catch(e => e); + expect(error.message).toContain('Dummy'); + }); + + it('should set the browser connected state', async ({browserType, remoteServer}) => { + const remote = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + expect(remote.isConnected()).toBe(true); + await remote.close(); + expect(remote.isConnected()).toBe(false); + }); + + it('should throw when used after isConnected returns false', async ({browserType, remoteServer}) => { + const remote = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const page = await remote.newPage(); + await Promise.all([ + remoteServer.close(), + new Promise(f => remote.once('disconnected', f)), + ]); + expect(remote.isConnected()).toBe(false); + const error = await page.evaluate('1 + 1').catch(e => e) as Error; + expect(error.message).toContain('has been closed'); + }); + + it('should reject navigation when browser closes', async ({browserType, remoteServer, server}) => { + server.setRoute('/one-style.css', () => {}); + const remote = await browserType.connect({ wsEndpoint: remoteServer.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!'); + }); + + it('should reject waitForSelector when browser closes', async ({browserType, remoteServer, server}) => { + server.setRoute('/empty.html', () => {}); + const remote = await browserType.connect({ wsEndpoint: remoteServer.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'); + }); + + it('should emit close events on pages and contexts', async ({browserType, remoteServer}) => { + const remote = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const context = await remote.newContext(); + const page = await context.newPage(); + let pageClosed = false; + page.on('close', () => pageClosed = true); + await Promise.all([ + new Promise(f => context.on('close', f)), + remoteServer.close() + ]); + expect(pageClosed).toBeTruthy(); + }); + + it('should terminate network waiters', async ({browserType, remoteServer, server}) => { + const remote = await browserType.connect({ wsEndpoint: remoteServer.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), + remoteServer.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 respect selectors', test => { + test.fail(true); + }, async ({ playwright, browserType, remoteServer }) => { + const mycss = () => ({ + create(root, target) {}, + query(root, selector) { + return root.querySelector(selector); + }, + queryAll(root: HTMLElement, selector: string) { + return Array.from(root.querySelectorAll(selector)); + } + }); + await utils.registerEngine(playwright, 'mycss', mycss); + + const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); + const page = await browser.newPage(); + await page.setContent(`
hello
`); + expect(await page.innerHTML('css=div')).toBe('hello'); + expect(await page.innerHTML('mycss=div')).toBe('hello'); + await browser.close(); + }); }); diff --git a/test/browsertype-launch-server.spec.ts b/test/browsertype-launch-server.spec.ts index 399b10ee6d..da54615cd1 100644 --- a/test/browsertype-launch-server.spec.ts +++ b/test/browsertype-launch-server.spec.ts @@ -17,38 +17,40 @@ import { options } from './playwright.fixtures'; -it.skip(options.WIRE)('should work', async ({browserType, defaultBrowserOptions}) => { - const browserServer = await browserType.launchServer(defaultBrowserOptions); - expect(browserServer.wsEndpoint()).not.toBe(null); - await browserServer.close(); -}); +describe.skip(options.WIRE)('lauch server', () => { + it('should work', async ({browserType, defaultBrowserOptions}) => { + const browserServer = await browserType.launchServer(defaultBrowserOptions); + expect(browserServer.wsEndpoint()).not.toBe(null); + await browserServer.close(); + }); -it.skip(options.WIRE)('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 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.skip(options.WIRE)('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 return child_process instance', async ({browserType, defaultBrowserOptions}) => { + const browserServer = await browserType.launchServer(defaultBrowserOptions); + expect(browserServer.process().pid).toBeGreaterThan(0); + await browserServer.close(); + }); -it.skip(options.WIRE)('should fire close event', async ({browserType, defaultBrowserOptions}) => { - const browserServer = await browserType.launchServer(defaultBrowserOptions); - const [result] = await Promise.all([ - new Promise(f => (browserServer as any).on('close', (exitCode, signal) => f({ exitCode, signal }))), - browserServer.close(), - ]); - expect(result['exitCode']).toBe(0); - expect(result['signal']).toBe(null); + it('should fire close event', async ({browserType, defaultBrowserOptions}) => { + const browserServer = await browserType.launchServer(defaultBrowserOptions); + const [result] = await Promise.all([ + new Promise(f => (browserServer as any).on('close', (exitCode, signal) => f({ exitCode, signal }))), + browserServer.close(), + ]); + expect(result['exitCode']).toBe(0); + expect(result['signal']).toBe(null); + }); }); diff --git a/test/browsertype-launch.spec.ts b/test/browsertype-launch.spec.ts index 007d790a7d..2e3f28383e 100644 --- a/test/browsertype-launch.spec.ts +++ b/test/browsertype-launch.spec.ts @@ -36,15 +36,18 @@ it('should throw if userDataDir option is passed', async ({browserType, defaultB expect(waitError.message).toContain('launchPersistentContext'); }); -it.skip(options.FIREFOX)('should throw if page argument is passed', async ({browserType, defaultBrowserOptions}) => { +it('should throw if page argument is passed', test => { + test.skip(options.FIREFOX); +}, 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.fixme(true)('should reject if launched browser fails immediately', async ({browserType, defaultBrowserOptions}) => { - // I'm getting ENCONRESET on this one. +it('should reject if launched browser fails immediately', test => { + test.fixme(`I'm getting ENCONRESET on this one.`); +}, async ({browserType, defaultBrowserOptions}) => { 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); @@ -58,7 +61,9 @@ it('should reject if executable path is invalid', async ({browserType, defaultBr expect(waitError.message).toContain('Failed to launch'); }); -it.skip(options.WIRE)('should handle timeout', async ({browserType, defaultBrowserOptions}) => { +it('should handle timeout', test => { + test.skip(options.WIRE); +}, 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.`); @@ -66,21 +71,27 @@ it.skip(options.WIRE)('should handle timeout', async ({browserType, defaultBrows expect(error.message).toContain(` pid=`); }); -it.skip(options.WIRE)('should handle exception', async ({browserType, defaultBrowserOptions}) => { +it('should handle exception', test => { + test.skip(options.WIRE); +}, 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(options.WIRE)('should report launch log', async ({browserType, defaultBrowserOptions}) => { +it('should report launch log', test => { + test.skip(options.WIRE); +}, 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}) => { +it('should accept objects as options', test => { + test.slow(); +}, async ({browserType, defaultBrowserOptions}) => { const browser = await browserType.launch({ ...defaultBrowserOptions, process } as any); await browser.close(); }); diff --git a/test/capabilities.spec.ts b/test/capabilities.spec.ts index 83b082a3ba..d6633b1533 100644 --- a/test/capabilities.spec.ts +++ b/test/capabilities.spec.ts @@ -17,7 +17,9 @@ import url from 'url'; import { options } from './playwright.fixtures'; -it.fail(options.WEBKIT && WIN)('Web Assembly should work', async function({page, server}) { +it('Web Assembly should work', test => { + test.fail(options.WEBKIT && WIN); +}, async function({page, server}) { await page.goto(server.PREFIX + '/wasm/table2.html'); expect(await page.evaluate('loadTable()')).toBe('42, 83'); }); @@ -48,7 +50,9 @@ it('should respect CSP', async ({page, server}) => { expect(await page.evaluate(() => window['testStatus'])).toBe('SUCCESS'); }); -it.fixme(options.WEBKIT && (WIN || LINUX))('should play video', async ({page, asset}) => { +it('should play video', test => { + test.fixme(options.WEBKIT && (WIN || LINUX)); +}, async ({page, asset}) => { // TODO: the test passes on Windows locally but fails on GitHub Action bot, // apparently due to a Media Pack issue in the Windows Server. // Also the test is very flaky on Linux WebKit. diff --git a/test/channels.spec.ts b/test/channels.spec.ts index eb9c01a7dc..6ff3e402e3 100644 --- a/test/channels.spec.ts +++ b/test/channels.spec.ts @@ -65,7 +65,9 @@ it('should scope context handles', async ({browserType, browser, server}) => { await expectScopeState(browser, GOLDEN_PRECONDITION); }); -it.skip(!options.CHROMIUM)('should scope CDPSession handles', async ({browserType, browser, server}) => { +it('should scope CDPSession handles', test => { + test.skip(!options.CHROMIUM); +}, async ({browserType, browser}) => { const GOLDEN_PRECONDITION = { _guid: '', objects: [ diff --git a/test/chromium-css-coverage.spec.ts b/test/chromium-css-coverage.spec.ts index fe33986d1f..5d21e3ed93 100644 --- a/test/chromium-css-coverage.spec.ts +++ b/test/chromium-css-coverage.spec.ts @@ -15,119 +15,121 @@ */ import { options } from './playwright.fixtures'; -it.skip(!options.CHROMIUM)('should work', async function({browserType, page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/simple.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/csscoverage/simple.html'); - expect(coverage[0].ranges).toEqual([ - {start: 1, end: 22} - ]); - const range = coverage[0].ranges[0]; - expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }'); -}); +describe.skip(!options.CHROMIUM)('oopif', () => { + it('should work', async function({browserType, page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/simple.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toContain('/csscoverage/simple.html'); + expect(coverage[0].ranges).toEqual([ + {start: 1, end: 22} + ]); + const range = coverage[0].ranges[0]; + expect(coverage[0].text.substring(range.start, range.end)).toBe('div { color: green; }'); + }); -it.skip(!options.CHROMIUM)('should report sourceURLs', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('nicename.css'); -}); + it('should report sourceURLs', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toBe('nicename.css'); + }); -it.skip(!options.CHROMIUM)('should report multiple stylesheets', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/multiple.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(2); - coverage.sort((a, b) => a.url.localeCompare(b.url)); - expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); - expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); -}); + it('should report multiple stylesheets', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/multiple.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(2); + coverage.sort((a, b) => a.url.localeCompare(b.url)); + expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); + expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); + }); -it.skip(!options.CHROMIUM)('should report stylesheets that have no coverage', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/unused.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('unused.css'); - expect(coverage[0].ranges.length).toBe(0); -}); + it('should report stylesheets that have no coverage', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/unused.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toBe('unused.css'); + expect(coverage[0].ranges.length).toBe(0); + }); -it.skip(!options.CHROMIUM)('should work with media queries', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.goto(server.PREFIX + '/csscoverage/media.html'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/csscoverage/media.html'); - expect(coverage[0].ranges).toEqual([ - {start: 17, end: 38} - ]); -}); + it('should work with media queries', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.goto(server.PREFIX + '/csscoverage/media.html'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(1); + expect(coverage[0].url).toContain('/csscoverage/media.html'); + expect(coverage[0].ranges).toEqual([ + {start: 17, end: 38} + ]); + }); -it.skip(!options.CHROMIUM)('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 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.skip(!options.CHROMIUM)('should ignore injected stylesheets', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.addStyleTag({content: 'body { margin: 10px;}'}); - // trigger style recalc - const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin); - expect(margin).toBe('10px'); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(0); -}); + it('should ignore injected stylesheets', async function({page, server}) { + await page.coverage.startCSSCoverage(); + await page.addStyleTag({content: 'body { margin: 10px;}'}); + // trigger style recalc + const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin); + expect(margin).toBe('10px'); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(0); + }); -it.skip(!options.CHROMIUM)('should report stylesheets across navigations', async function({page, server}) { - await page.coverage.startCSSCoverage({resetOnNavigation: false}); - await page.goto(server.PREFIX + '/csscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(2); -}); + it('should report stylesheets across navigations', async function({page, server}) { + await page.coverage.startCSSCoverage({resetOnNavigation: false}); + await page.goto(server.PREFIX + '/csscoverage/multiple.html'); + await page.goto(server.EMPTY_PAGE); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(2); + }); -it.skip(!options.CHROMIUM)('should NOT report scripts across navigations', async function({page, server}) { - await page.coverage.startCSSCoverage(); // Enabled by default. - await page.goto(server.PREFIX + '/csscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopCSSCoverage(); - expect(coverage.length).toBe(0); -}); + it('should NOT report scripts across navigations', async function({page, server}) { + await page.coverage.startCSSCoverage(); // Enabled by default. + await page.goto(server.PREFIX + '/csscoverage/multiple.html'); + await page.goto(server.EMPTY_PAGE); + const coverage = await page.coverage.stopCSSCoverage(); + expect(coverage.length).toBe(0); + }); -it.skip(!options.CHROMIUM)('should work with a recently loaded stylesheet', async function({page, server}) { - await page.coverage.startCSSCoverage(); - await page.evaluate(async url => { - document.body.textContent = 'hello, world'; + 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); + 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/test/chromium-js-coverage.spec.ts b/test/chromium-js-coverage.spec.ts index 5f73338afc..2247eac856 100644 --- a/test/chromium-js-coverage.spec.ts +++ b/test/chromium-js-coverage.spec.ts @@ -15,82 +15,87 @@ */ import { options } from './playwright.fixtures'; -it.skip(options.CHROMIUM)('should be missing', async function({page, server}) { +it('should be missing', test => { + test.skip(options.CHROMIUM); +}, +async function({page}) { expect(page.coverage).toBe(null); }); -it.skip(!options.CHROMIUM)('should work', async function({browserType, page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toContain('/jscoverage/simple.html'); - expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1); -}); - -it.skip(!options.CHROMIUM)('should report sourceURLs', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(1); - expect(coverage[0].url).toBe('nicename.js'); -}); - -it.skip(!options.CHROMIUM)('should ignore eval() scripts by default', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/eval.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(1); -}); - -it.skip(!options.CHROMIUM)('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) { - await page.coverage.startJSCoverage({reportAnonymousScripts: true}); - await page.goto(server.PREFIX + '/jscoverage/eval.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.find(entry => entry.url === '').source).toBe('console.log("foo")'); - expect(coverage.length).toBe(2); -}); - -it.skip(!options.CHROMIUM)('should ignore playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) { - await page.coverage.startJSCoverage({reportAnonymousScripts: true}); - await page.goto(server.EMPTY_PAGE); - await page.evaluate('console.log("foo")'); - await page.evaluate(() => console.log('bar')); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(0); -}); - -it.skip(!options.CHROMIUM)('should report multiple scripts', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.PREFIX + '/jscoverage/multiple.html'); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(2); - coverage.sort((a, b) => a.url.localeCompare(b.url)); - expect(coverage[0].url).toContain('/jscoverage/script1.js'); - expect(coverage[1].url).toContain('/jscoverage/script2.js'); -}); - -it.skip(!options.CHROMIUM)('should report scripts across navigations when disabled', async function({page, server}) { - await page.coverage.startJSCoverage({resetOnNavigation: false}); - await page.goto(server.PREFIX + '/jscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(2); -}); - -it.skip(!options.CHROMIUM)('should NOT report scripts across navigations when enabled', async function({page, server}) { - await page.coverage.startJSCoverage(); // Enabled by default. - await page.goto(server.PREFIX + '/jscoverage/multiple.html'); - await page.goto(server.EMPTY_PAGE); - const coverage = await page.coverage.stopJSCoverage(); - expect(coverage.length).toBe(0); -}); - -it.skip(!options.CHROMIUM)('should not hang when there is a debugger statement', async function({page, server}) { - await page.coverage.startJSCoverage(); - await page.goto(server.EMPTY_PAGE); - await page.evaluate(() => { - debugger; // eslint-disable-line no-debugger +describe.skip(!options.CHROMIUM)('oopif', () => { + 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); }); - await page.coverage.stopJSCoverage(); -}); + + 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 ignore playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) { + await page.coverage.startJSCoverage({reportAnonymousScripts: true}); + await page.goto(server.EMPTY_PAGE); + await page.evaluate('console.log("foo")'); + await page.evaluate(() => console.log('bar')); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(0); + }); + + it('should report multiple scripts', async function({page, server}) { + await page.coverage.startJSCoverage(); + await page.goto(server.PREFIX + '/jscoverage/multiple.html'); + const coverage = await page.coverage.stopJSCoverage(); + expect(coverage.length).toBe(2); + coverage.sort((a, b) => a.url.localeCompare(b.url)); + expect(coverage[0].url).toContain('/jscoverage/script1.js'); + expect(coverage[1].url).toContain('/jscoverage/script2.js'); + }); + + 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(); + }); +}); \ No newline at end of file diff --git a/test/chromium/chromium.spec.ts b/test/chromium/chromium.spec.ts index 604576d385..43a1348aa1 100644 --- a/test/chromium/chromium.spec.ts +++ b/test/chromium/chromium.spec.ts @@ -16,72 +16,74 @@ import { options } from '../playwright.fixtures'; import type { ChromiumBrowserContext } from '../..'; -it.skip(!options.CHROMIUM)('should create a worker from a service worker', async ({page, server, context}) => { - const [worker] = await Promise.all([ - (context as ChromiumBrowserContext).waitForEvent('serviceworker'), - page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') - ]); - expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); -}); - -it.skip(!options.CHROMIUM)('serviceWorkers() should return current workers', async ({page, server, context}) => { - const [worker1] = await Promise.all([ - (context as ChromiumBrowserContext).waitForEvent('serviceworker'), - page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') - ]); - let workers = (context as ChromiumBrowserContext).serviceWorkers(); - expect(workers.length).toBe(1); - - const [worker2] = await Promise.all([ - (context as ChromiumBrowserContext).waitForEvent('serviceworker'), - page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html') - ]); - workers = (context as ChromiumBrowserContext).serviceWorkers(); - expect(workers.length).toBe(2); - expect(workers).toContain(worker1); - expect(workers).toContain(worker2); -}); - -it.skip(!options.CHROMIUM)('should not create a worker from a shared worker', async ({page, server, context}) => { - await page.goto(server.EMPTY_PAGE); - let serviceWorkerCreated; - (context as ChromiumBrowserContext).once('serviceworker', () => serviceWorkerCreated = true); - await page.evaluate(() => { - new SharedWorker('data:text/javascript,console.log("hi")'); - }); - expect(serviceWorkerCreated).not.toBeTruthy(); -}); - -it.skip(!options.CHROMIUM)('should close service worker together with the context', async ({browser, server}) => { - const context = await browser.newContext() as ChromiumBrowserContext; - 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(!options.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);'); +describe.skip(!options.CHROMIUM)('chromium', () => { + it('should create a worker from a service worker', async ({page, server, context}) => { + const [worker] = await Promise.all([ + (context as ChromiumBrowserContext).waitForEvent('serviceworker'), + page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') + ]); + expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); }); - 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'); + it('serviceWorkers() should return current workers', async ({page, server, context}) => { + const [worker1] = await Promise.all([ + (context as ChromiumBrowserContext).waitForEvent('serviceworker'), + page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') + ]); + let workers = (context as ChromiumBrowserContext).serviceWorkers(); + expect(workers.length).toBe(1); + + const [worker2] = await Promise.all([ + (context as ChromiumBrowserContext).waitForEvent('serviceworker'), + page.goto(server.CROSS_PROCESS_PREFIX + '/serviceworkers/empty/sw.html') + ]); + workers = (context as ChromiumBrowserContext).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 as ChromiumBrowserContext).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() as ChromiumBrowserContext; + 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('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.spec.ts b/test/chromium/launcher.spec.ts index 8ea77c5962..21159f99cb 100644 --- a/test/chromium/launcher.spec.ts +++ b/test/chromium/launcher.spec.ts @@ -20,21 +20,27 @@ import utils from '../utils'; import type { ChromiumBrowser, ChromiumBrowserContext } from '../..'; const { makeUserDataDir, removeUserDataDir } = utils; -it.skip(options.WIRE || !options.CHROMIUM)('should throw with remote-debugging-pipe argument', async ({browserType, defaultBrowserOptions}) => { +it('should throw with remote-debugging-pipe argument', test => { + test.skip(options.WIRE || !options.CHROMIUM); +}, 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(options.WIRE || !options.CHROMIUM)('should not throw with remote-debugging-port argument', async ({browserType, defaultBrowserOptions}) => { +it('should not throw with remote-debugging-port argument', test => { + test.skip(options.WIRE || !options.CHROMIUM); +}, 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(!options.CHROMIUM || options.WIRE || WIN)('should open devtools when "devtools: true" option is given', async ({browserType, defaultBrowserOptions}) => { +it('should open devtools when "devtools: true" option is given', test => { + test.skip(!options.CHROMIUM || options.WIRE || WIN); +}, async ({browserType, defaultBrowserOptions}) => { let devtoolsCallback; const devtoolsPromise = new Promise(f => devtoolsCallback = f); const __testHookForDevTools = devtools => devtools.__testHookOnBinding = parsed => { @@ -50,7 +56,9 @@ it.skip(!options.CHROMIUM || options.WIRE || WIN)('should open devtools when "de await browser.close(); }); -it.skip(!options.CHROMIUM)('should return background pages', async ({browserType, defaultBrowserOptions}) => { +it('should return background pages', test => { + test.skip(!options.CHROMIUM); +}, async ({browserType, defaultBrowserOptions}) => { const userDataDir = await makeUserDataDir(); const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension'); const extensionOptions = {...defaultBrowserOptions, @@ -72,7 +80,9 @@ it.skip(!options.CHROMIUM)('should return background pages', async ({browserType await removeUserDataDir(userDataDir); }); -it.skip(!options.CHROMIUM)('should not create pages automatically', async ({browserType, defaultBrowserOptions}) => { +it('should not create pages automatically', test => { + test.skip(!options.CHROMIUM); +}, async ({browserType, defaultBrowserOptions}) => { const browser = await browserType.launch(defaultBrowserOptions); const browserSession = await (browser as ChromiumBrowser).newBrowserCDPSession(); const targets = []; diff --git a/test/chromium/oopif.spec.ts b/test/chromium/oopif.spec.ts index ecd345e7eb..379b6ff023 100644 --- a/test/chromium/oopif.spec.ts +++ b/test/chromium/oopif.spec.ts @@ -26,276 +26,281 @@ registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, te await browser.close(); }); -it.skip(!options.CHROMIUM)('should report oopif frames', async function({browser, page, 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.skip(!options.CHROMIUM)('should handle oopif detach', async function({browser, page, 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.skip(!options.CHROMIUM)('should handle remote -> local -> remote transitions', async function({browser, page, 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.fixme(options.CHROMIUM).skip(!options.CHROMIUM)('should get the proper viewport', async ({browser, page, server}) => { - 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(!options.CHROMIUM)('should expose function', async ({browser, page, 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); +describe.skip(!options.CHROMIUM)('oopif', () => { + it('should report oopif frames', async function({browser, page, 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'); }); - expect(result).toBe(36); -}); -it.skip(!options.CHROMIUM)('should emulate media', async ({browser, page, 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.skip(!options.CHROMIUM)('should emulate offline', async ({browser, page, context, 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 context.setOffline(true); - expect(await oopif.evaluate(() => navigator.onLine)).toBe(false); -}); - -it.skip(!options.CHROMIUM)('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.skip(!options.CHROMIUM)('should respect route', async ({browser, page, server}) => { - let intercepted = false; - await page.route('**/digits/0.png', route => { - intercepted = true; - route.continue(); + it('should handle oopif detach', async function({browser, page, 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); }); - 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(!options.CHROMIUM)('should take screenshot', async ({browser, page, server, golden}) => { - 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()).toMatchImage(golden('screenshot-oopif.png'), { threshold: 0.3 }); -}); - -it.skip(!options.CHROMIUM)('should load oopif iframes with subresources and route', async function({browser, page, server, context}) { - await page.route('**/*', route => route.continue()); - await page.goto(server.PREFIX + '/dynamic-oopif.html'); - expect(await countOOPIFs(browser)).toBe(1); -}); - -it.skip(!options.CHROMIUM)('should report main requests', async function({browser, page, 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.skip(!options.CHROMIUM)('should support exposeFunction', async function({browser, context, page, server}) { - 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(() => 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.skip(!options.CHROMIUM)('should support addInitScript', async function({browser, context, page, server}) { - 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(!options.CHROMIUM)('should click a button when it overlays oopif', async function({browser, page, 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.skip(!options.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'}); + it('should handle remote -> local -> remote transitions', async function({browser, page, 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); }); - 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); + + it('should get the proper viewport', test => { + test.fixme(options.CHROMIUM); + test.skip(!options.CHROMIUM); + }, async ({browser, page, server}) => { + 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.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(!options.CHROMIUM)('ElementHandle.boundingBox() should work', async function({browser, page, 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'; + it('should expose function', async ({browser, page, 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); }); - 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(!options.CHROMIUM)('should click', async function({browser, page, 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'; + it('should emulate media', async ({browser, page, 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); }); - 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); + it('should emulate offline', async ({browser, page, context, 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 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 ({browser, page, 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 ({browser, page, server, golden}) => { + 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()).toMatchImage(golden('screenshot-oopif.png'), { threshold: 0.3 }); + }); + + it('should load oopif iframes with subresources and route', async function({browser, page, server, context}) { + 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({browser, page, 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({browser, context, page, server}) { + 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(() => 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({browser, context, page, server}) { + 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({browser, page, 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, 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({browser, page, 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({browser, page, 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); + }); }); async function countOOPIFs(browser) { diff --git a/test/chromium/session.spec.ts b/test/chromium/session.spec.ts index 97cc1d8165..95252ce046 100644 --- a/test/chromium/session.spec.ts +++ b/test/chromium/session.spec.ts @@ -16,105 +16,107 @@ import { options } from '../playwright.fixtures'; import type { ChromiumBrowserContext, ChromiumBrowser } from '../../types/types'; -it.skip(!options.CHROMIUM)('should work', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).newCDPSession(page); +describe.skip(!options.CHROMIUM)('session', () => { + it('should work', async function({page}) { + const client = await (page.context() as ChromiumBrowserContext).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(!options.CHROMIUM)('should send events', async function({page, server}) { - const client = await (page.context() as ChromiumBrowserContext).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(!options.CHROMIUM)('should only accept a page', async function({page}) { - const error = await (page.context() as ChromiumBrowserContext).newCDPSession(page.context() as any).catch(e => e); - expect(error.message).toContain('page: expected Page'); -}); - -it.skip(!options.CHROMIUM)('should enable and disable domains independently', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).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(!options.CHROMIUM)('should be able to detach session', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).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 browser or context has been closed'); -}); - -it.skip(!options.CHROMIUM)('should throw nice errors', async function({page}) { - const client = await (page.context() as ChromiumBrowserContext).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' as any); - } -}); - -it.skip(!options.CHROMIUM)('should not break page.close()', async function({browser}) { - const context = await browser.newContext(); - const page = await context.newPage(); - const session = await (page.context() as ChromiumBrowserContext).newCDPSession(page); - await session.detach(); - await page.close(); - await context.close(); -}); - -it.skip(!options.CHROMIUM)('should detach when page closes', async function({browser}) { - const context = await browser.newContext() as ChromiumBrowserContext; - 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(!options.CHROMIUM)('should work', async function({browser}) { - const session = await (browser as ChromiumBrowser).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(); + 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() as ChromiumBrowserContext).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}) { + const error = await (page.context() as ChromiumBrowserContext).newCDPSession(page.context() as any).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() as ChromiumBrowserContext).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() as ChromiumBrowserContext).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 browser or context has been closed'); + }); + + it('should throw nice errors', async function({page}) { + const client = await (page.context() as ChromiumBrowserContext).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' as any); + } + }); + + it('should not break page.close()', async function({browser}) { + const context = await browser.newContext(); + const page = await context.newPage(); + const session = await (page.context() as ChromiumBrowserContext).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() as ChromiumBrowserContext; + 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('should work', async function({browser}) { + const session = await (browser as ChromiumBrowser).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.spec.ts b/test/chromium/tracing.spec.ts index 93cecf677e..6afa494e58 100644 --- a/test/chromium/tracing.spec.ts +++ b/test/chromium/tracing.spec.ts @@ -34,57 +34,59 @@ registerFixture('outputFile', async ({tmpDir}, test) => { fs.unlinkSync(outputFile); }); -it.skip(!options.CHROMIUM)('should output a trace', async ({browser, page, server, outputFile}) => { - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); - await page.goto(server.PREFIX + '/grid.html'); - await (browser as ChromiumBrowser).stopTracing(); - expect(fs.existsSync(outputFile)).toBe(true); -}); +describe.skip(!options.CHROMIUM)('oopif', () => { + it('should output a trace', async ({browser, page, server, outputFile}) => { + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); + await page.goto(server.PREFIX + '/grid.html'); + await (browser as ChromiumBrowser).stopTracing(); + expect(fs.existsSync(outputFile)).toBe(true); + }); -it.skip(!options.CHROMIUM)('should create directories as needed', async ({browser, page, server, tmpDir}) => { - const filePath = path.join(tmpDir, 'these', 'are', 'directories'); - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath}); - await page.goto(server.PREFIX + '/grid.html'); - await (browser as ChromiumBrowser).stopTracing(); - expect(fs.existsSync(filePath)).toBe(true); -}); + it('should create directories as needed', async ({browser, page, server, tmpDir}) => { + const filePath = path.join(tmpDir, 'these', 'are', 'directories'); + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath}); + await page.goto(server.PREFIX + '/grid.html'); + await (browser as ChromiumBrowser).stopTracing(); + expect(fs.existsSync(filePath)).toBe(true); + }); -it.skip(!options.CHROMIUM)('should run with custom categories if provided', async ({browser, page, outputFile}) => { - await (browser as ChromiumBrowser).startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); - await (browser as ChromiumBrowser).stopTracing(); + it('should run with custom categories if provided', async ({browser, page, outputFile}) => { + await (browser as ChromiumBrowser).startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']}); + await (browser as ChromiumBrowser).stopTracing(); - const traceJson = JSON.parse(fs.readFileSync(outputFile).toString()); - expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); -}); + const traceJson = JSON.parse(fs.readFileSync(outputFile).toString()); + expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); + }); -it.skip(!options.CHROMIUM)('should throw if tracing on two pages', async ({browser, page, outputFile}) => { - await (browser as ChromiumBrowser).startTracing(page, {path: outputFile}); - const newPage = await browser.newPage(); - let error = null; - await (browser as ChromiumBrowser).startTracing(newPage, {path: outputFile}).catch(e => error = e); - await newPage.close(); - expect(error).toBeTruthy(); - await (browser as ChromiumBrowser).stopTracing(); -}); + it('should throw if tracing on two pages', async ({browser, page, outputFile}) => { + await (browser as ChromiumBrowser).startTracing(page, {path: outputFile}); + const newPage = await browser.newPage(); + let error = null; + await (browser as ChromiumBrowser).startTracing(newPage, {path: outputFile}).catch(e => error = e); + await newPage.close(); + expect(error).toBeTruthy(); + await (browser as ChromiumBrowser).stopTracing(); + }); -it.skip(!options.CHROMIUM)('should return a buffer', async ({browser, page, server, outputFile}) => { - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); - await page.goto(server.PREFIX + '/grid.html'); - const trace = await (browser as ChromiumBrowser).stopTracing(); - const buf = fs.readFileSync(outputFile); - expect(trace.toString()).toEqual(buf.toString()); -}); + it('should return a buffer', async ({browser, page, server, outputFile}) => { + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); + await page.goto(server.PREFIX + '/grid.html'); + const trace = await (browser as ChromiumBrowser).stopTracing(); + const buf = fs.readFileSync(outputFile); + expect(trace.toString()).toEqual(buf.toString()); + }); -it.skip(!options.CHROMIUM)('should work without options', async ({browser, page, server}) => { - await (browser as ChromiumBrowser).startTracing(page); - await page.goto(server.PREFIX + '/grid.html'); - const trace = await (browser as ChromiumBrowser).stopTracing(); - expect(trace).toBeTruthy(); -}); + it('should work without options', async ({browser, page, server}) => { + await (browser as ChromiumBrowser).startTracing(page); + await page.goto(server.PREFIX + '/grid.html'); + const trace = await (browser as ChromiumBrowser).stopTracing(); + expect(trace).toBeTruthy(); + }); -it.skip(!options.CHROMIUM)('should support a buffer without a path', async ({browser, page, server}) => { - await (browser as ChromiumBrowser).startTracing(page, {screenshots: true}); - await page.goto(server.PREFIX + '/grid.html'); - const trace = await (browser as ChromiumBrowser).stopTracing(); - expect(trace.toString()).toContain('screenshot'); + it('should support a buffer without a path', async ({browser, page, server}) => { + await (browser as ChromiumBrowser).startTracing(page, {screenshots: true}); + await page.goto(server.PREFIX + '/grid.html'); + const trace = await (browser as ChromiumBrowser).stopTracing(); + expect(trace.toString()).toContain('screenshot'); + }); }); diff --git a/test/click-react.spec.ts b/test/click-react.spec.ts index 22222374df..1dd697bcce 100644 --- a/test/click-react.spec.ts +++ b/test/click-react.spec.ts @@ -20,7 +20,9 @@ declare const renderComponent; declare const e; declare const MyButton; -it.fail(true)('should report that selector does not match anymore', async ({page, server}) => { +it('should report that selector does not match anymore', test => { + test.fail(true); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/react.html'); await page.evaluate(() => { renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })])); @@ -39,7 +41,9 @@ it.fail(true)('should report that selector does not match anymore', async ({page expect(error.message).toContain('element does not match the selector anymore'); }); -it.fixme(true)('should not retarget the handle when element is recycled', async ({page, server}) => { +it('should not retarget the handle when element is recycled', test => { + test.fixme(true); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/react.html'); await page.evaluate(() => { renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })])); @@ -66,7 +70,9 @@ it('should timeout when click opens alert', async ({page, server}) => { await dialog.dismiss(); }); -it.fixme(true)('should retarget when element is recycled during hit testing', async ({page, server}) => { +it('should retarget when element is recycled during hit testing', test => { + test.fixme(true); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/react.html'); await page.evaluate(() => { renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })])); @@ -81,7 +87,9 @@ it.fixme(true)('should retarget when element is recycled during hit testing', as expect(await page.evaluate('window.button2')).toBe(undefined); }); -it.fixme(true)('should retarget when element is recycled before enabled check', async ({page, server}) => { +it('should retarget when element is recycled before enabled check', test => { + test.fixme(true); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/react.html'); await page.evaluate(() => { renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })])); diff --git a/test/click-timeout-1.spec.ts b/test/click-timeout-1.spec.ts index e5fa26369f..ace4ca8f33 100644 --- a/test/click-timeout-1.spec.ts +++ b/test/click-timeout-1.spec.ts @@ -17,7 +17,9 @@ import { options } from './playwright.fixtures'; -it.skip(options.WIRE)('should avoid side effects after timeout', async ({page, server}) => { +it('should avoid side effects after timeout', test => { + test.skip(options.WIRE); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); const error = await page.click('button', { timeout: 2000, __testHookBeforePointerAction: () => new Promise(f => setTimeout(f, 2500))} as any).catch(e => e); await page.waitForTimeout(5000); // Give it some time to click after the test hook is done waiting. diff --git a/test/click-timeout-3.spec.ts b/test/click-timeout-3.spec.ts index 39378e4648..1b1b1193c5 100644 --- a/test/click-timeout-3.spec.ts +++ b/test/click-timeout-3.spec.ts @@ -17,7 +17,9 @@ import { options } from './playwright.fixtures'; -it.skip(options.WIRE)('should fail when element jumps during hit testing', async ({page, server}) => { +it('should fail when element jumps during hit testing', test => { + test.skip(options.WIRE); +}, async ({page}) => { await page.setContent(''); let clicked = false; const handle = await page.$('button'); diff --git a/test/click.spec.ts b/test/click.spec.ts index 5c04939148..5d52b1a6ab 100644 --- a/test/click.spec.ts +++ b/test/click.spec.ts @@ -322,7 +322,9 @@ it('should click the button inside an iframe', async ({page, server}) => { expect(await frame.evaluate(() => window['result'])).toBe('Clicked'); }); -it.fixme(options.CHROMIUM || options.WEBKIT)('should click the button with fixed position inside an iframe', async ({page, server}) => { +it('should click the button with fixed position inside an iframe', test => { + test.fixme(options.CHROMIUM || options.WEBKIT); +}, async ({page, server}) => { // @see https://github.com/GoogleChrome/puppeteer/issues/4110 // @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390 // @see https://chromium-review.googlesource.com/c/chromium/src/+/1742784 @@ -401,7 +403,9 @@ it('should click a button in scrolling container with offset', async ({page, ser expect(await page.evaluate('offsetY')).toBe(options.WEBKIT ? 1910 + 8 : 1910); }); -it.skip(options.FIREFOX)('should click the button with offset with page scale', async ({browser, server}) => { +it('should click the button with offset with page scale', test => { + test.skip(options.FIREFOX); +}, async ({browser, server}) => { const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true }); const page = await context.newPage(); await page.goto(server.PREFIX + '/input/button.html'); diff --git a/test/defaultbrowsercontext-1.spec.ts b/test/defaultbrowsercontext-1.spec.ts index 419b0e3452..b9bb7b7083 100644 --- a/test/defaultbrowsercontext-1.spec.ts +++ b/test/defaultbrowsercontext-1.spec.ts @@ -170,8 +170,9 @@ it('should support offline option', async ({server, launchPersistent}) => { expect(error).toBeTruthy(); }); -it.skip(true)('should support acceptDownloads option', async ({server, launchPersistent}) => { - // TODO: unskip once we support downloads in persistent context. +it('should support acceptDownloads option', test => { + test.skip('Unskip once we support downloads in persistent context.'); +}, async ({server, launchPersistent}) => { const {page} = await launchPersistent({acceptDownloads: true}); server.setRoute('/download', (req, res) => { res.setHeader('Content-Type', 'application/octet-stream'); diff --git a/test/defaultbrowsercontext-2.spec.ts b/test/defaultbrowsercontext-2.spec.ts index c0ac08b517..cc85f74633 100644 --- a/test/defaultbrowsercontext-2.spec.ts +++ b/test/defaultbrowsercontext-2.spec.ts @@ -26,7 +26,9 @@ it('should support hasTouch option', async ({server, launchPersistent}) => { expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); }); -it.skip(options.FIREFOX)('should work in persistent context', async ({server, launchPersistent}) => { +it('should work in persistent context', test => { + test.skip(options.FIREFOX); +}, async ({server, launchPersistent}) => { // Firefox does not support mobile. const {page} = await launchPersistent({viewport: {width: 320, height: 480}, isMobile: true}); await page.goto(server.PREFIX + '/empty.html'); @@ -75,7 +77,9 @@ it('should support extraHTTPHeaders option', async ({server, launchPersistent}) expect(request.headers['foo']).toBe('bar'); }); -it.flaky(options.CHROMIUM)('should accept userDataDir', async ({launchPersistent, tmpDir}) => { +it('should accept userDataDir', test => { + test.flaky(options.CHROMIUM); +}, async ({launchPersistent, tmpDir}) => { const {context} = await launchPersistent(); // Note: we need an open page to make sure its functional. expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0); @@ -85,7 +89,9 @@ it.flaky(options.CHROMIUM)('should accept userDataDir', async ({launchPersistent await removeUserDataDir(tmpDir); }); -it.slow()('should restore state from userDataDir', async ({browserType, defaultBrowserOptions, server, launchPersistent}) => { +it('should restore state from userDataDir', test => { + test.slow(); +}, async ({browserType, defaultBrowserOptions, server, launchPersistent}) => { const userDataDir = await makeUserDataDir(); const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions); const page = await browserContext.newPage(); @@ -111,7 +117,10 @@ it.slow()('should restore state from userDataDir', async ({browserType, defaultB await removeUserDataDir(userDataDir2); }); -it.slow().flaky(options.CHROMIUM && WIN)('should restore cookies from userDataDir', async ({browserType, defaultBrowserOptions, server, launchPersistent}) => { +it('should restore cookies from userDataDir', test => { + test.slow(); + test.flaky(options.CHROMIUM && WIN); +}, async ({browserType, defaultBrowserOptions, server, launchPersistent}) => { const userDataDir = await makeUserDataDir(); const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions); const page = await browserContext.newPage(); @@ -147,13 +156,17 @@ it('should have default URL when launching browser', async ({launchPersistent}) expect(urls).toEqual(['about:blank']); }); -it.skip(options.FIREFOX)('should throw if page argument is passed', async ({browserType, defaultBrowserOptions, server, tmpDir}) => { +it('should throw if page argument is passed', test => { + test.skip(options.FIREFOX); +}, async ({browserType, defaultBrowserOptions, server, tmpDir}) => { const options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] }; const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e); expect(error.message).toContain('can not specify page'); }); -it.skip(options.WIRE)('should have passed URL when launching with ignoreDefaultArgs: true', async ({browserType, defaultBrowserOptions, server, tmpDir, toImpl}) => { +it('should have passed URL when launching with ignoreDefaultArgs: true', test => { + test.skip(options.WIRE); +}, async ({browserType, defaultBrowserOptions, server, tmpDir, toImpl}) => { const args = toImpl(browserType)._defaultArgs(defaultBrowserOptions, 'persistent', tmpDir, 0).filter(a => a !== 'about:blank'); const options = { ...defaultBrowserOptions, @@ -169,13 +182,17 @@ it.skip(options.WIRE)('should have passed URL when launching with ignoreDefaultA await browserContext.close(); }); -it.skip(options.WIRE)('should handle timeout', async ({browserType, defaultBrowserOptions, tmpDir}) => { +it('should handle timeout', test => { + test.skip(options.WIRE); +}, async ({browserType, defaultBrowserOptions, tmpDir}) => { const options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) }; const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e); expect(error.message).toContain(`browserType.launchPersistentContext: Timeout 5000ms exceeded.`); }); -it.skip(options.WIRE)('should handle exception', async ({browserType, defaultBrowserOptions, tmpDir}) => { +it('should handle exception', test => { + test.skip(options.WIRE); +}, async ({browserType, defaultBrowserOptions, tmpDir}) => { const e = new Error('Dummy'); const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } }; const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e); @@ -190,7 +207,9 @@ it('should fire close event for a persistent context', async ({launchPersistent} expect(closed).toBe(true); }); -it.skip(!options.CHROMIUM)('coverage should work', async ({server, launchPersistent}) => { +it('coverage should work', test => { + test.skip(!options.CHROMIUM); +}, async ({server, launchPersistent}) => { const {page} = await launchPersistent(); await page.coverage.startJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); @@ -200,7 +219,9 @@ it.skip(!options.CHROMIUM)('coverage should work', async ({server, launchPersist expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(1); }); -it.skip(options.CHROMIUM)('coverage should be missing', async ({launchPersistent}) => { +it('coverage should be missing', test => { + test.skip(options.CHROMIUM); +}, async ({launchPersistent}) => { const {page} = await launchPersistent(); expect(page.coverage).toBe(null); }); diff --git a/test/dialog.spec.ts b/test/dialog.spec.ts index f5b3e84759..a4903bce4a 100644 --- a/test/dialog.spec.ts +++ b/test/dialog.spec.ts @@ -62,7 +62,9 @@ it('should dismiss the confirm prompt', async ({page}) => { expect(result).toBe(false); }); -it.fixme(options.WEBKIT && MAC)('should be able to close context with open alert', async ({browser}) => { +it('should be able to close context with open alert', test => { + test.fixme(options.WEBKIT && MAC); +}, async ({browser}) => { const context = await browser.newContext(); const page = await context.newPage(); const alertPromise = page.waitForEvent('dialog'); diff --git a/test/dispatchevent.spec.ts b/test/dispatchevent.spec.ts index d7a5459a0a..f6b986b861 100644 --- a/test/dispatchevent.spec.ts +++ b/test/dispatchevent.spec.ts @@ -126,7 +126,9 @@ it('should be atomic', async ({playwright, page}) => { expect(await page.evaluate(() => window['_clicked'])).toBe(true); }); -it.fail(options.WEBKIT)('should dispatch drag drop events', async ({page, server}) => { +it('should dispatch drag drop events', test => { + test.fail(options.WEBKIT); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/drag-n-drop.html'); const dataTransfer = await page.evaluateHandle(() => new DataTransfer()); await page.dispatchEvent('#source', 'dragstart', { dataTransfer }); @@ -138,7 +140,9 @@ it.fail(options.WEBKIT)('should dispatch drag drop events', async ({page, server }, {source, target})).toBeTruthy(); }); -it.fail(options.WEBKIT)('should dispatch drag drop events', async ({page, server}) => { +it('should dispatch drag drop events', test => { + test.fail(options.WEBKIT); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/drag-n-drop.html'); const dataTransfer = await page.evaluateHandle(() => new DataTransfer()); const source = await page.$('#source'); diff --git a/test/download.spec.ts b/test/download.spec.ts index 6cfe1d6395..811416d626 100644 --- a/test/download.spec.ts +++ b/test/download.spec.ts @@ -144,7 +144,9 @@ it('should create subdirectories when saving to non-existent user-specified path await page.close(); }); -it.skip(options.WIRE)('should save when connected remotely', async ({tmpDir, server, browserType, remoteServer}) => { +it('should save when connected remotely', test => { + test.skip(options.WIRE); +}, async ({tmpDir, server, browserType, remoteServer}) => { const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); const page = await browser.newPage({ acceptDownloads: true }); await page.setContent(`download`); @@ -188,7 +190,9 @@ it('should error when saving after deletion', async ({tmpDir, browser, server}) await page.close(); }); -it.skip(options.WIRE)('should error when saving after deletion when connected remotely', async ({tmpDir, server, browserType, remoteServer}) => { +it('should error when saving after deletion when connected remotely', test => { + test.skip(options.WIRE); +}, async ({tmpDir, server, browserType, remoteServer}) => { const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); const page = await browser.newPage({ acceptDownloads: true }); await page.setContent(`download`); @@ -250,7 +254,9 @@ it(`should report download path within page.on('download', …) handler for Blob expect(fs.readFileSync(path).toString()).toBe('Hello world'); await page.close(); }); -it.fixme(options.FIREFOX || options.WEBKIT)('should report alt-click downloads', async ({browser, server}) => { +it('should report alt-click downloads', test => { + test.fixme(options.FIREFOX || options.WEBKIT); +}, async ({browser, server}) => { // Firefox does not download on alt-click by default. // Our WebKit embedder does not download on alt-click, although Safari does. server.setRoute('/download', (req, res) => { @@ -271,7 +277,9 @@ it.fixme(options.FIREFOX || options.WEBKIT)('should report alt-click downloads', await page.close(); }); -it.fixme(options.CHROMIUM && !options.HEADLESS)('should report new window downloads', async ({browser, server}) => { +it('should report new window downloads', test => { + test.fixme(options.CHROMIUM && !options.HEADLESS); +}, async ({browser, server}) => { // TODO: - the test fails in headful Chromium as the popup page gets closed along // with the session before download completed event arrives. // - WebKit doesn't close the popup page diff --git a/test/electron/electron-app.spec.ts b/test/electron/electron-app.spec.ts index 5a851eea39..dceb82c22f 100644 --- a/test/electron/electron-app.spec.ts +++ b/test/electron/electron-app.spec.ts @@ -20,105 +20,106 @@ import './electron.fixture'; import path from 'path'; const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron'; - -it.skip(!options.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')], +describe.skip(!options.CHROMIUM)('electron app', () => { + 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'); }); - 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(!options.CHROMIUM)('should script application', async ({ application }) => { - const appPath = await application.evaluate(async ({ app }) => app.getAppPath()); - expect(appPath).toContain('electron'); -}); + it('should script application', async ({ application }) => { + const appPath = await application.evaluate(async ({ app }) => app.getAppPath()); + expect(appPath).toContain('electron'); + }); -it.skip(!options.CHROMIUM)('should create window', async ({ application }) => { - const [ page ] = await Promise.all([ - application.waitForEvent('window'), + 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); + await createPage(2); + await createPage(3); + await page1.close(); + 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(() => window['copy'])).toBe(42); + }); + + it('should expose function', async ({ application }) => { + 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(() => window['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 1'); - }) - ]); - await page.waitForLoadState('domcontentloaded'); - expect(await page.title()).toBe('Hello World 1'); -}); - -it.skip(!options.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(!options.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); - await createPage(2); - await createPage(3); - await page1.close(); - 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(!options.CHROMIUM)('should route network', async ({ application }) => { - await application.context().route('**/empty.html', (route, request) => { - route.fulfill({ - status: 200, - contentType: 'text/html', - body: 'Hello World', + window.loadURL('data:text/html,Hello World!'); }); + const window = await application.firstWindow(); + expect(await window.title()).toBe('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(!options.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(() => window['copy'])).toBe(42); -}); - -it.skip(!options.CHROMIUM)('should expose function', async ({ application }) => { - 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(() => window['result'])).toBe(42); -}); - -it.skip(!options.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!'); + 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); }); - const window = await application.firstWindow(); - expect(await window.title()).toBe('Hello World!'); -}); - -it.skip(!options.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.ts b/test/electron/electron-window.spec.ts index ab63283911..40280eaec4 100644 --- a/test/electron/electron-window.spec.ts +++ b/test/electron/electron-window.spec.ts @@ -17,31 +17,33 @@ import { options } from '../playwright.fixtures'; import './electron.fixture'; -it.skip(!options.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(!options.CHROMIUM)('should check the box', async ({window}) => { - await window.setContent(``); - await window.check('input'); - expect(await window.evaluate('checkbox.checked')).toBe(true); -}); - -it.skip(!options.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(!options.CHROMIUM)('should type into a textarea', async ({window, server}) => { - await window.evaluate(() => { - const textarea = document.createElement('textarea'); - document.body.appendChild(textarea); - textarea.focus(); +describe.skip(!options.CHROMIUM)('electron window', () => { + 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); }); - 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/elementhandle-bounding-box.spec.ts b/test/elementhandle-bounding-box.spec.ts index e2ddd1beb6..826d913288 100644 --- a/test/elementhandle-bounding-box.spec.ts +++ b/test/elementhandle-bounding-box.spec.ts @@ -17,7 +17,9 @@ import { options } from './playwright.fixtures'; -it.fail(options.FIREFOX && !options.HEADLESS)('should work', async ({ page, server }) => { +it('should work', test => { + test.fail(options.FIREFOX && !options.HEADLESS); +}, async ({ page, server }) => { await page.setViewportSize({ width: 500, height: 500 }); await page.goto(server.PREFIX + '/grid.html'); const elementHandle = await page.$('.box:nth-of-type(13)'); @@ -64,7 +66,9 @@ it('should work with SVG nodes', async ({ page, server }) => { expect(pwBoundingBox).toEqual(webBoundingBox); }); -it.skip(options.FIREFOX)('should work with page scale', async ({ browser, server }) => { +it('should work with page scale', test => { + test.skip(options.FIREFOX); +}, async ({ browser, server }) => { const context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true }); const page = await context.newPage(); await page.goto(server.PREFIX + '/input/button.html'); diff --git a/test/elementhandle-owner-frame.spec.ts b/test/elementhandle-owner-frame.spec.ts index 0dc05f66bf..2e3f6ae89a 100644 --- a/test/elementhandle-owner-frame.spec.ts +++ b/test/elementhandle-owner-frame.spec.ts @@ -35,7 +35,9 @@ it('should work for cross-process iframes', async ({ page, server }) => { expect(await elementHandle.ownerFrame()).toBe(frame); }); -it.flaky(WIN && options.WEBKIT)('should work for document', async ({ page, server }) => { +it('should work for document', test => { + test.flaky(WIN && options.WEBKIT); +}, async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); const frame = page.frames()[1]; diff --git a/test/elementhandle-screenshot.spec.ts b/test/elementhandle-screenshot.spec.ts index 0a6916541e..89232dcfb2 100644 --- a/test/elementhandle-screenshot.spec.ts +++ b/test/elementhandle-screenshot.spec.ts @@ -24,359 +24,373 @@ import fs from 'fs'; // Firefox headful produces a different image. const ffheadful = options.FIREFOX && !options.HEADLESS; -it.skip(ffheadful)('should work', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); -}); - -it.skip(ffheadful)('should take into account padding and border', async ({page, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.setContent(` -
oooo
- -
- `); - const elementHandle = await page.$('div#d'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-padding-border.png')); -}); - -it.skip(ffheadful)('should capture full element when larger than viewport in parallel', async ({page, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - - await page.setContent(` -
oooo
- -
-
-
- `); - const elementHandles = await page.$$('div.to-screenshot'); - const promises = elementHandles.map(handle => handle.screenshot()); - const screenshots = await Promise.all(promises); - expect(screenshots[2]).toMatchImage(golden('screenshot-element-larger-than-viewport.png')); - - await utils.verifyViewport(page, 500, 500); -}); - -it.skip(ffheadful)('should capture full element when larger than viewport', async ({page, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - - await page.setContent(` -
oooo
- -
-
-
- `); - const elementHandle = await page.$('div.to-screenshot'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-larger-than-viewport.png')); - - await utils.verifyViewport(page, 500, 500); -}); - -it.skip(ffheadful)('should scroll element into view', async ({page, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.setContent(` -
oooo
- -
-
- `); - const elementHandle = await page.$('div.to-screenshot'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-scrolled-into-view.png')); -}); - -it.skip(ffheadful)('should scroll 15000px into view', async ({page, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.setContent(` -
oooo
- -
-
- `); - const elementHandle = await page.$('div.to-screenshot'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-scrolled-into-view.png')); -}); - -it.skip(ffheadful)('should work with a rotated element', async ({page, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.setContent(`
 
`); - const elementHandle = await page.$('div'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-rotate.png')); -}); - -it.skip(ffheadful)('should fail to screenshot a detached element', async ({page, server}) => { - await page.setContent('

remove this

'); - const elementHandle = await page.$('h1'); - await page.evaluate(element => element.remove(), elementHandle); - const screenshotError = await elementHandle.screenshot().catch(error => error); - expect(screenshotError.message).toContain('Element is not attached to the DOM'); -}); - -it.skip(ffheadful)('should timeout waiting for visible', async ({page, server}) => { - await page.setContent('
'); - const div = await page.$('div'); - const error = await div.screenshot({ timeout: 3000 }).catch(e => e); - expect(error.message).toContain('elementHandle.screenshot: Timeout 3000ms exceeded'); - expect(error.message).toContain('element is not visible'); -}); - -it.skip(ffheadful)('should wait for visible', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); - await elementHandle.evaluate(e => e.style.visibility = 'hidden'); - let done = false; - const promise = elementHandle.screenshot().then(buffer => { - done = true; - return buffer; +describe.skip(ffheadful)('element screenshot', () => { + it('should work', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + await page.evaluate(() => window.scrollBy(50, 100)); + const elementHandle = await page.$('.box:nth-of-type(3)'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); }); - for (let i = 0; i < 10; i++) - await page.evaluate(() => new Promise(f => requestAnimationFrame(f))); - expect(done).toBe(false); - await elementHandle.evaluate(e => e.style.visibility = 'visible'); - const screenshot = await promise; - expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); -}); -it.skip(ffheadful)('should work for an element with fractional dimensions', async ({page, golden}) => { - await page.setContent('
'); - const elementHandle = await page.$('div'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-fractional.png')); -}); - -it.skip(options.FIREFOX)('should work with a mobile viewport', async ({browser, server, golden}) => { - const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-mobile.png')); - await context.close(); -}); - -it.skip(options.FIREFOX)('should work with device scale factor', async ({browser, server, golden}) => { - const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-mobile-dsf.png')); - await context.close(); -}); - -it.skip(ffheadful)('should work for an element with an offset', async ({page, golden}) => { - await page.setContent('
'); - const elementHandle = await page.$('div'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-fractional-offset.png')); -}); - -it.skip(ffheadful)('should take screenshots when default viewport is null', async ({server, browser}) => { - const context = await browser.newContext({ viewport: null }); - const page = await context.newPage(); - await page.setContent(`
`); - const windowSize = await page.evaluate(() => ({ width: window.innerWidth * window.devicePixelRatio, height: window.innerHeight * window.devicePixelRatio })); - const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); - - const screenshot = await page.screenshot(); - expect(screenshot).toBeInstanceOf(Buffer); - const decoded = PNG.sync.read(screenshot); - expect(decoded.width).toBe(windowSize.width); - expect(decoded.height).toBe(windowSize.height); - - const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); - expect(sizeBefore.width).toBe(sizeAfter.width); - expect(sizeBefore.height).toBe(sizeAfter.height); - await context.close(); -}); - -it.skip(ffheadful)('should take fullPage screenshots when default viewport is null', async ({server, browser}) => { - const context = await browser.newContext({ viewport: null }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); - const screenshot = await page.screenshot({ - fullPage: true + it('should take into account padding and border', async ({page, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.setContent(` +
oooo
+ +
+ `); + const elementHandle = await page.$('div#d'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-padding-border.png')); }); - expect(screenshot).toBeInstanceOf(Buffer); - const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); - expect(sizeBefore.width).toBe(sizeAfter.width); - expect(sizeBefore.height).toBe(sizeAfter.height); - await context.close(); -}); + it('should capture full element when larger than viewport in parallel', async ({page, golden}) => { + await page.setViewportSize({width: 500, height: 500}); -it.skip(ffheadful)('should restore default viewport after fullPage screenshot', async ({ browser }) => { - const context = await browser.newContext({ viewport: { width: 456, height: 789 } }); - const page = await context.newPage(); - await utils.verifyViewport(page, 456, 789); - const screenshot = await page.screenshot({ fullPage: true }); - expect(screenshot).toBeInstanceOf(Buffer); - await utils.verifyViewport(page, 456, 789); - await context.close(); -}); + await page.setContent(` +
oooo
+ +
+
+
+ `); + const elementHandles = await page.$$('div.to-screenshot'); + const promises = elementHandles.map(handle => handle.screenshot()); + const screenshots = await Promise.all(promises); + expect(screenshots[2]).toMatchImage(golden('screenshot-element-larger-than-viewport.png')); -it.skip(ffheadful || options.WIRE)('should restore viewport after page screenshot and exception', async ({ browser, server }) => { - const context = await browser.newContext({ viewport: { width: 350, height: 360 } }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - const __testHookBeforeScreenshot = () => { throw new Error('oh my'); }; - const error = await page.screenshot({ fullPage: true, __testHookBeforeScreenshot } as any).catch(e => e); - expect(error.message).toContain('oh my'); - await utils.verifyViewport(page, 350, 360); - await context.close(); -}); - -it.skip(ffheadful || options.WIRE)('should restore viewport after page screenshot and timeout', async ({ browser, server }) => { - const context = await browser.newContext({ viewport: { width: 350, height: 360 } }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - const __testHookAfterScreenshot = () => new Promise(f => setTimeout(f, 5000)); - const error = await page.screenshot({ fullPage: true, __testHookAfterScreenshot, timeout: 3000 } as any).catch(e => e); - expect(error.message).toContain('page.screenshot: Timeout 3000ms exceeded'); - await utils.verifyViewport(page, 350, 360); - await page.setViewportSize({ width: 400, height: 400 }); - await page.waitForTimeout(3000); // Give it some time to wrongly restore previous viewport. - await utils.verifyViewport(page, 400, 400); - await context.close(); -}); - -it.skip(ffheadful)('should take element screenshot when default viewport is null and restore back', async ({server, browser}) => { - const context = await browser.newContext({viewport: null}); - const page = await context.newPage(); - await page.setContent(` -
oooo
- -
-
-
- `); - const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); - const elementHandle = await page.$('div.to-screenshot'); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toBeInstanceOf(Buffer); - const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); - expect(sizeBefore.width).toBe(sizeAfter.width); - expect(sizeBefore.height).toBe(sizeAfter.height); - await context.close(); -}); - -it.skip(ffheadful || options.WIRE)('should restore viewport after element screenshot and exception', async ({server, browser}) => { - const context = await browser.newContext({ viewport: { width: 350, height: 360 } }); - const page = await context.newPage(); - await page.setContent(`
`); - const elementHandle = await page.$('div'); - const __testHookBeforeScreenshot = () => { throw new Error('oh my'); }; - const error = await elementHandle.screenshot({ __testHookBeforeScreenshot } as any).catch(e => e); - expect(error.message).toContain('oh my'); - await utils.verifyViewport(page, 350, 360); - await context.close(); -}); - -it.skip(ffheadful).flaky(options.WEBKIT && !options.HEADLESS && LINUX)('should wait for element to stop moving', async ({ page, server, golden }) => { - await page.setViewportSize({ width: 500, height: 500 }); - await page.goto(server.PREFIX + '/grid.html'); - const elementHandle = await page.$('.box:nth-of-type(3)'); - await elementHandle.evaluate(e => { - e.classList.add('animation'); - return new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))); + await utils.verifyViewport(page, 500, 500); }); - const screenshot = await elementHandle.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); -}); -it.skip(ffheadful)('should take screenshot of disabled button', async ({page}) => { - await page.setViewportSize({ width: 500, height: 500 }); - await page.setContent(``); - const button = await page.$('button'); - const screenshot = await button.screenshot(); - expect(screenshot).toBeInstanceOf(Buffer); -}); + it('should capture full element when larger than viewport', async ({page, golden}) => { + await page.setViewportSize({width: 500, height: 500}); -it.skip(ffheadful)('path option should create subdirectories', async ({page, server, golden, tmpDir}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(50, 100)); - const elementHandle = await page.$('.box:nth-of-type(3)'); - const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png'); - await elementHandle.screenshot({path: outputPath}); - expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-element-bounding-box.png')); + await page.setContent(` +
oooo
+ +
+
+
+ `); + const elementHandle = await page.$('div.to-screenshot'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-larger-than-viewport.png')); + + await utils.verifyViewport(page, 500, 500); + }); + + it('should scroll element into view', async ({page, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.setContent(` +
oooo
+ +
+
+ `); + const elementHandle = await page.$('div.to-screenshot'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-scrolled-into-view.png')); + }); + + it('should scroll 15000px into view', async ({page, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.setContent(` +
oooo
+ +
+
+ `); + const elementHandle = await page.$('div.to-screenshot'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-scrolled-into-view.png')); + }); + + it('should work with a rotated element', async ({page, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.setContent(`
 
`); + const elementHandle = await page.$('div'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-rotate.png')); + }); + + it('should fail to screenshot a detached element', async ({page, server}) => { + await page.setContent('

remove this

'); + const elementHandle = await page.$('h1'); + await page.evaluate(element => element.remove(), elementHandle); + const screenshotError = await elementHandle.screenshot().catch(error => error); + expect(screenshotError.message).toContain('Element is not attached to the DOM'); + }); + + it('should timeout waiting for visible', async ({page, server}) => { + await page.setContent('
'); + const div = await page.$('div'); + const error = await div.screenshot({ timeout: 3000 }).catch(e => e); + expect(error.message).toContain('elementHandle.screenshot: Timeout 3000ms exceeded'); + expect(error.message).toContain('element is not visible'); + }); + + it('should wait for visible', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + await page.evaluate(() => window.scrollBy(50, 100)); + const elementHandle = await page.$('.box:nth-of-type(3)'); + await elementHandle.evaluate(e => e.style.visibility = 'hidden'); + let done = false; + const promise = elementHandle.screenshot().then(buffer => { + done = true; + return buffer; + }); + for (let i = 0; i < 10; i++) + await page.evaluate(() => new Promise(f => requestAnimationFrame(f))); + expect(done).toBe(false); + await elementHandle.evaluate(e => e.style.visibility = 'visible'); + const screenshot = await promise; + expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); + }); + + it('should work for an element with fractional dimensions', async ({page, golden}) => { + await page.setContent('
'); + const elementHandle = await page.$('div'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-fractional.png')); + }); + + it('should work with a mobile viewport', test => { + test.skip(options.FIREFOX); + }, async ({browser, server, golden}) => { + const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + await page.evaluate(() => window.scrollBy(50, 100)); + const elementHandle = await page.$('.box:nth-of-type(3)'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-mobile.png')); + await context.close(); + }); + + it('should work with device scale factor', test => { + test.skip(options.FIREFOX); + }, async ({browser, server, golden}) => { + const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + await page.evaluate(() => window.scrollBy(50, 100)); + const elementHandle = await page.$('.box:nth-of-type(3)'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-mobile-dsf.png')); + await context.close(); + }); + + it('should work for an element with an offset', async ({page, golden}) => { + await page.setContent('
'); + const elementHandle = await page.$('div'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-fractional-offset.png')); + }); + + it('should take screenshots when default viewport is null', async ({server, browser}) => { + const context = await browser.newContext({ viewport: null }); + const page = await context.newPage(); + await page.setContent(`
`); + const windowSize = await page.evaluate(() => ({ width: window.innerWidth * window.devicePixelRatio, height: window.innerHeight * window.devicePixelRatio })); + const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); + + const screenshot = await page.screenshot(); + expect(screenshot).toBeInstanceOf(Buffer); + const decoded = PNG.sync.read(screenshot); + expect(decoded.width).toBe(windowSize.width); + expect(decoded.height).toBe(windowSize.height); + + const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); + expect(sizeBefore.width).toBe(sizeAfter.width); + expect(sizeBefore.height).toBe(sizeAfter.height); + await context.close(); + }); + + it('should take fullPage screenshots when default viewport is null', async ({server, browser}) => { + const context = await browser.newContext({ viewport: null }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); + const screenshot = await page.screenshot({ + fullPage: true + }); + expect(screenshot).toBeInstanceOf(Buffer); + + const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); + expect(sizeBefore.width).toBe(sizeAfter.width); + expect(sizeBefore.height).toBe(sizeAfter.height); + await context.close(); + }); + + it('should restore default viewport after fullPage screenshot', async ({ browser }) => { + const context = await browser.newContext({ viewport: { width: 456, height: 789 } }); + const page = await context.newPage(); + await utils.verifyViewport(page, 456, 789); + const screenshot = await page.screenshot({ fullPage: true }); + expect(screenshot).toBeInstanceOf(Buffer); + await utils.verifyViewport(page, 456, 789); + await context.close(); + }); + + it('should restore viewport after page screenshot and exception', test => { + test.skip(options.WIRE); + }, async ({ browser, server }) => { + const context = await browser.newContext({ viewport: { width: 350, height: 360 } }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + const __testHookBeforeScreenshot = () => { throw new Error('oh my'); }; + const error = await page.screenshot({ fullPage: true, __testHookBeforeScreenshot } as any).catch(e => e); + expect(error.message).toContain('oh my'); + await utils.verifyViewport(page, 350, 360); + await context.close(); + }); + + it('should restore viewport after page screenshot and timeout', test => { + test.skip(options.WIRE); + }, async ({ browser, server }) => { + const context = await browser.newContext({ viewport: { width: 350, height: 360 } }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + const __testHookAfterScreenshot = () => new Promise(f => setTimeout(f, 5000)); + const error = await page.screenshot({ fullPage: true, __testHookAfterScreenshot, timeout: 3000 } as any).catch(e => e); + expect(error.message).toContain('page.screenshot: Timeout 3000ms exceeded'); + await utils.verifyViewport(page, 350, 360); + await page.setViewportSize({ width: 400, height: 400 }); + await page.waitForTimeout(3000); // Give it some time to wrongly restore previous viewport. + await utils.verifyViewport(page, 400, 400); + await context.close(); + }); + + it('should take element screenshot when default viewport is null and restore back', async ({server, browser}) => { + const context = await browser.newContext({viewport: null}); + const page = await context.newPage(); + await page.setContent(` +
oooo
+ +
+
+
+ `); + const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); + const elementHandle = await page.$('div.to-screenshot'); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toBeInstanceOf(Buffer); + const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); + expect(sizeBefore.width).toBe(sizeAfter.width); + expect(sizeBefore.height).toBe(sizeAfter.height); + await context.close(); + }); + + it('should restore viewport after element screenshot and exception', test => { + test.skip(options.WIRE); + }, async ({server, browser}) => { + const context = await browser.newContext({ viewport: { width: 350, height: 360 } }); + const page = await context.newPage(); + await page.setContent(`
`); + const elementHandle = await page.$('div'); + const __testHookBeforeScreenshot = () => { throw new Error('oh my'); }; + const error = await elementHandle.screenshot({ __testHookBeforeScreenshot } as any).catch(e => e); + expect(error.message).toContain('oh my'); + await utils.verifyViewport(page, 350, 360); + await context.close(); + }); + + it('should wait for element to stop moving', test => { + test.flaky(options.WEBKIT && !options.HEADLESS && LINUX); + }, async ({ page, server, golden }) => { + await page.setViewportSize({ width: 500, height: 500 }); + await page.goto(server.PREFIX + '/grid.html'); + const elementHandle = await page.$('.box:nth-of-type(3)'); + await elementHandle.evaluate(e => { + e.classList.add('animation'); + return new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))); + }); + const screenshot = await elementHandle.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); + }); + + it('should take screenshot of disabled button', async ({page}) => { + await page.setViewportSize({ width: 500, height: 500 }); + await page.setContent(``); + const button = await page.$('button'); + const screenshot = await button.screenshot(); + expect(screenshot).toBeInstanceOf(Buffer); + }); + + it('path option should create subdirectories', async ({page, server, golden, tmpDir}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + await page.evaluate(() => window.scrollBy(50, 100)); + const elementHandle = await page.$('.box:nth-of-type(3)'); + const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png'); + await elementHandle.screenshot({path: outputPath}); + expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-element-bounding-box.png')); + }); }); diff --git a/test/elementhandle-wait-for-element-state.spec.ts b/test/elementhandle-wait-for-element-state.spec.ts index 5cea794946..4fab1e0868 100644 --- a/test/elementhandle-wait-for-element-state.spec.ts +++ b/test/elementhandle-wait-for-element-state.spec.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import './playwright.fixtures'; +import { options } from './playwright.fixtures'; async function giveItAChanceToResolve(page) { for (let i = 0; i < 5; i++) @@ -114,7 +114,9 @@ it('should wait for disabled button', async ({page}) => { await promise; }); -it('should wait for stable position', async ({page, server}) => { +it('should wait for stable position', test => { + test.fixme(options.FIREFOX && LINUX); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/input/button.html'); const button = await page.$('button'); await page.$eval('button', button => { diff --git a/test/emulation-focus.spec.ts b/test/emulation-focus.spec.ts index a3974f27d1..607e76a2d1 100644 --- a/test/emulation-focus.spec.ts +++ b/test/emulation-focus.spec.ts @@ -101,7 +101,9 @@ it('should change document.activeElement', async ({page, server}) => { expect(active).toEqual(['INPUT', 'TEXTAREA']); }); -it.skip(options.FIREFOX && !options.HEADLESS)('should not affect screenshots', async ({page, server, golden}) => { +it('should not affect screenshots', test => { + test.skip(options.FIREFOX && !options.HEADLESS); +}, async ({page, server, golden}) => { // Firefox headful produces a different image. const page2 = await page.context().newPage(); await Promise.all([ diff --git a/test/firefox/launcher.spec.ts b/test/firefox/launcher.spec.ts index 0ba764b249..8d25b6ec57 100644 --- a/test/firefox/launcher.spec.ts +++ b/test/firefox/launcher.spec.ts @@ -15,7 +15,9 @@ */ import { options } from '../playwright.fixtures'; -it.skip(!options.FIREFOX)('should pass firefox user preferences', async ({browserType, defaultBrowserOptions}) => { +it('should pass firefox user preferences', test => { + test.skip(!options.FIREFOX); +}, async ({browserType, defaultBrowserOptions}) => { const browser = await browserType.launch({ ...defaultBrowserOptions, firefoxUserPrefs: { diff --git a/test/fixtures.spec.ts b/test/fixtures.spec.ts index 8c4bb6f7b0..cf68f74111 100644 --- a/test/fixtures.spec.ts +++ b/test/fixtures.spec.ts @@ -20,7 +20,9 @@ import './remoteServer.fixture'; import { execSync } from 'child_process'; import path from 'path'; -it.slow()('should close the browser when the node process closes', async ({remoteServer}) => { +it('should close the browser when the node process closes', test => { + test.slow(); +}, async ({remoteServer}) => { if (WIN) execSync(`taskkill /pid ${remoteServer.child().pid} /T /F`); else @@ -30,74 +32,76 @@ it.slow()('should close the browser when the node process closes', async ({remot // so we don't check it here. }); -// Cannot reliably send signals on Windows. -it.skip(WIN || !options.HEADLESS).slow()('should report browser close signal', async ({remoteServer}) => { - const pid = await remoteServer.out('pid'); - process.kill(-pid, 'SIGTERM'); - expect(await remoteServer.out('exitCode')).toBe('null'); - expect(await remoteServer.out('signal')).toBe('SIGTERM'); - process.kill(remoteServer.child().pid); - await remoteServer.childExitCode(); -}); +describe.skip(WIN || !options.HEADLESS).slow()('fixtures', () => { + // Cannot reliably send signals on Windows. + it('should report browser close signal', async ({remoteServer}) => { + const pid = await remoteServer.out('pid'); + process.kill(-pid, 'SIGTERM'); + expect(await remoteServer.out('exitCode')).toBe('null'); + expect(await remoteServer.out('signal')).toBe('SIGTERM'); + process.kill(remoteServer.child().pid); + await remoteServer.childExitCode(); + }); -it.skip(WIN || !options.HEADLESS).slow()('should report browser close signal 2', async ({remoteServer}) => { - const pid = await remoteServer.out('pid'); - process.kill(-pid, 'SIGKILL'); - expect(await remoteServer.out('exitCode')).toBe('null'); - expect(await remoteServer.out('signal')).toBe('SIGKILL'); - process.kill(remoteServer.child().pid); - await remoteServer.childExitCode(); -}); + it('should report browser close signal 2', async ({remoteServer}) => { + const pid = await remoteServer.out('pid'); + process.kill(-pid, 'SIGKILL'); + expect(await remoteServer.out('exitCode')).toBe('null'); + expect(await remoteServer.out('signal')).toBe('SIGKILL'); + process.kill(remoteServer.child().pid); + await remoteServer.childExitCode(); + }); -it.skip(WIN || !options.HEADLESS).slow()('should close the browser on SIGINT', async ({remoteServer}) => { - process.kill(remoteServer.child().pid, 'SIGINT'); - expect(await remoteServer.out('exitCode')).toBe('0'); - expect(await remoteServer.out('signal')).toBe('null'); - expect(await remoteServer.childExitCode()).toBe(130); -}); + it('should close the browser on SIGINT', async ({remoteServer}) => { + process.kill(remoteServer.child().pid, 'SIGINT'); + expect(await remoteServer.out('exitCode')).toBe('0'); + expect(await remoteServer.out('signal')).toBe('null'); + expect(await remoteServer.childExitCode()).toBe(130); + }); -it.skip(WIN || !options.HEADLESS).slow()('should close the browser on SIGTERM', async ({remoteServer}) => { - process.kill(remoteServer.child().pid, 'SIGTERM'); - expect(await remoteServer.out('exitCode')).toBe('0'); - expect(await remoteServer.out('signal')).toBe('null'); - expect(await remoteServer.childExitCode()).toBe(0); -}); + it('should close the browser on SIGTERM', async ({remoteServer}) => { + process.kill(remoteServer.child().pid, 'SIGTERM'); + expect(await remoteServer.out('exitCode')).toBe('0'); + expect(await remoteServer.out('signal')).toBe('null'); + expect(await remoteServer.childExitCode()).toBe(0); + }); -it.skip(WIN || !options.HEADLESS).slow()('should close the browser on SIGHUP', async ({remoteServer}) => { - process.kill(remoteServer.child().pid, 'SIGHUP'); - expect(await remoteServer.out('exitCode')).toBe('0'); - expect(await remoteServer.out('signal')).toBe('null'); - expect(await remoteServer.childExitCode()).toBe(0); -}); + it('should close the browser on SIGHUP', async ({remoteServer}) => { + process.kill(remoteServer.child().pid, 'SIGHUP'); + expect(await remoteServer.out('exitCode')).toBe('0'); + expect(await remoteServer.out('signal')).toBe('null'); + expect(await remoteServer.childExitCode()).toBe(0); + }); -it.skip(WIN || !options.HEADLESS).slow()('should kill the browser on double SIGINT', async ({stallingRemoteServer}) => { - const remoteServer = stallingRemoteServer; - process.kill(remoteServer.child().pid, 'SIGINT'); - await remoteServer.out('stalled'); - process.kill(remoteServer.child().pid, 'SIGINT'); - expect(await remoteServer.out('exitCode')).toBe('null'); - expect(await remoteServer.out('signal')).toBe('SIGKILL'); - expect(await remoteServer.childExitCode()).toBe(130); -}); + it('should kill the browser on double SIGINT', async ({stallingRemoteServer}) => { + const remoteServer = stallingRemoteServer; + process.kill(remoteServer.child().pid, 'SIGINT'); + await remoteServer.out('stalled'); + process.kill(remoteServer.child().pid, 'SIGINT'); + expect(await remoteServer.out('exitCode')).toBe('null'); + expect(await remoteServer.out('signal')).toBe('SIGKILL'); + expect(await remoteServer.childExitCode()).toBe(130); + }); -it.skip(WIN || !options.HEADLESS).slow()('should kill the browser on SIGINT + SIGTERM', async ({stallingRemoteServer}) => { - const remoteServer = stallingRemoteServer; - process.kill(remoteServer.child().pid, 'SIGINT'); - await remoteServer.out('stalled'); - process.kill(remoteServer.child().pid, 'SIGTERM'); - expect(await remoteServer.out('exitCode')).toBe('null'); - expect(await remoteServer.out('signal')).toBe('SIGKILL'); - expect(await remoteServer.childExitCode()).toBe(0); -}); + it('should kill the browser on SIGINT + SIGTERM', async ({stallingRemoteServer}) => { + const remoteServer = stallingRemoteServer; + process.kill(remoteServer.child().pid, 'SIGINT'); + await remoteServer.out('stalled'); + process.kill(remoteServer.child().pid, 'SIGTERM'); + expect(await remoteServer.out('exitCode')).toBe('null'); + expect(await remoteServer.out('signal')).toBe('SIGKILL'); + expect(await remoteServer.childExitCode()).toBe(0); + }); -it.skip(WIN || !options.HEADLESS).slow()('should kill the browser on SIGTERM + SIGINT', async ({stallingRemoteServer}) => { - const remoteServer = stallingRemoteServer; - process.kill(remoteServer.child().pid, 'SIGTERM'); - await remoteServer.out('stalled'); - process.kill(remoteServer.child().pid, 'SIGINT'); - expect(await remoteServer.out('exitCode')).toBe('null'); - expect(await remoteServer.out('signal')).toBe('SIGKILL'); - expect(await remoteServer.childExitCode()).toBe(130); + it('should kill the browser on SIGTERM + SIGINT', async ({stallingRemoteServer}) => { + const remoteServer = stallingRemoteServer; + process.kill(remoteServer.child().pid, 'SIGTERM'); + await remoteServer.out('stalled'); + process.kill(remoteServer.child().pid, 'SIGINT'); + expect(await remoteServer.out('exitCode')).toBe('null'); + expect(await remoteServer.out('signal')).toBe('SIGKILL'); + expect(await remoteServer.childExitCode()).toBe(130); + }); }); it('caller file path', async ({}) => { diff --git a/test/focus.spec.ts b/test/focus.spec.ts index 44a446ee2b..edd85f8127 100644 --- a/test/focus.spec.ts +++ b/test/focus.spec.ts @@ -15,7 +15,9 @@ */ import { options } from './playwright.fixtures'; -it.skip(options.FIREFOX)('should work', async function({page}) { +it('should work', test => { + test.skip(options.FIREFOX); +}, async function({page}) { await page.setContent(`
`); expect(await page.evaluate(() => document.activeElement.nodeName)).toBe('BODY'); await page.focus('#d1'); @@ -75,9 +77,10 @@ it('should traverse focus in all directions', async function({page}) { expect(await page.evaluate(() => (document.activeElement as HTMLInputElement).value)).toBe('1'); }); -// Chromium and WebKit both have settings for tab traversing all links, but -// it is only on by default in WebKit. -it.skip(!MAC || !options.WEBKIT)('should traverse only form elements', async function({page}) { +it('should traverse only form elements', test => { + test.skip(!MAC || !options.WEBKIT, + 'Chromium and WebKit both have settings for tab traversing all links, but it is only on by default in WebKit.'); +}, async function({page}) { await page.setContent(` diff --git a/test/frame-evaluate.spec.ts b/test/frame-evaluate.spec.ts index dc251107c7..3d8ef60286 100644 --- a/test/frame-evaluate.spec.ts +++ b/test/frame-evaluate.spec.ts @@ -42,7 +42,9 @@ function expectContexts(pageImpl, count) { expect(pageImpl._delegate._contextIdToContext.size).toBe(count); } -it.skip(options.WIRE)('should dispose context on navigation', async ({ page, server, toImpl }) => { +it('should dispose context on navigation', test => { + test.skip(options.WIRE); +}, async ({ page, server, toImpl }) => { await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames().length).toBe(2); expectContexts(toImpl(page), 4); @@ -50,7 +52,9 @@ it.skip(options.WIRE)('should dispose context on navigation', async ({ page, ser expectContexts(toImpl(page), 2); }); -it.skip(options.WIRE)('should dispose context on cross-origin navigation', async ({ page, server, toImpl }) => { +it('should dispose context on cross-origin navigation', test => { + test.skip(options.WIRE); +}, async ({ page, server, toImpl }) => { await page.goto(server.PREFIX + '/frames/one-frame.html'); expect(page.frames().length).toBe(2); expectContexts(toImpl(page), 4); @@ -126,7 +130,9 @@ it('should be isolated between frames', async ({page, server}) => { expect(a2).toBe(2); }); -it.fail(options.CHROMIUM || options.FIREFOX)('should work in iframes that failed initial navigation', async ({page, server}) => { +it('should work in iframes that failed initial navigation', test => { + test.fail(options.CHROMIUM || options.FIREFOX); +}, async ({page}) => { // - Firefox does not report domcontentloaded for the iframe. // - Chromium and Firefox report empty url. // - Chromium does not report main/utility worlds for the iframe. @@ -147,7 +153,9 @@ it.fail(options.CHROMIUM || options.FIREFOX)('should work in iframes that failed expect(await page.frames()[1].$('div')).toBeTruthy(); }); -it.fixme(options.CHROMIUM)('should work in iframes that interrupted initial javascript url navigation', async ({page, server}) => { +it('should work in iframes that interrupted initial javascript url navigation', test => { + test.fixme(options.CHROMIUM); +}, async ({page, server}) => { // Chromium does not report isolated world for the iframe. await page.goto(server.EMPTY_PAGE); await page.evaluate(() => { diff --git a/test/frame-hierarchy.spec.ts b/test/frame-hierarchy.spec.ts index d34fb6c7cb..67cb511a44 100644 --- a/test/frame-hierarchy.spec.ts +++ b/test/frame-hierarchy.spec.ts @@ -171,7 +171,9 @@ it('should report different frame instance when frame re-attaches', async ({page expect(frame1).not.toBe(frame2); }); -it.fixme(options.FIREFOX)('should refuse to display x-frame-options:deny iframe', async ({page, server}) => { +it('should refuse to display x-frame-options:deny iframe', test => { + test.fixme(options.FIREFOX); +}, async ({page, server}) => { server.setRoute('/x-frame-options-deny.html', async (req, res) => { res.setHeader('Content-Type', 'text/html'); res.setHeader('X-Frame-Options', 'DENY'); diff --git a/test/headful.spec.ts b/test/headful.spec.ts index 8d5cb7d440..171cce2dd1 100644 --- a/test/headful.spec.ts +++ b/test/headful.spec.ts @@ -26,7 +26,11 @@ it('should have default url when launching browser', async ({browserType, defaul await browserContext.close(); }); -it.fail(WIN && options.CHROMIUM).flaky(options.FIREFOX).slow()('headless should be able to read cookies written by headful', async ({browserType, defaultBrowserOptions, server}) => { +it('headless should be able to read cookies written by headful', test => { + test.fail(WIN && options.CHROMIUM); + test.flaky(options.FIREFOX); + test.slow(); +}, async ({browserType, defaultBrowserOptions, server}) => { // see https://github.com/microsoft/playwright/issues/717 const userDataDir = await makeUserDataDir(); // Write a cookie in headful chrome @@ -46,7 +50,9 @@ it.fail(WIN && options.CHROMIUM).flaky(options.FIREFOX).slow()('headless should expect(cookie).toBe('foo=true'); }); -it.slow()('should close browser with beforeunload page', async ({browserType, defaultBrowserOptions, server, tmpDir}) => { +it('should close browser with beforeunload page', test => { + test.slow(); +}, async ({browserType, defaultBrowserOptions, server, tmpDir}) => { const browserContext = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, headless: false}); const page = await browserContext.newPage(); await page.goto(server.PREFIX + '/beforeunload.html'); @@ -128,7 +134,9 @@ it('should(not) block third party cookies', async ({browserType, defaultBrowserO await browser.close(); }); -it.fixme(options.WEBKIT)('should not override viewport size when passed null', async function({browserType, defaultBrowserOptions, server}) { +it('should not override viewport size when passed null', test => { + test.fixme(options.WEBKIT); +}, async function({browserType, defaultBrowserOptions, server}) { // Our WebKit embedder does not respect window features. const browser = await browserType.launch({...defaultBrowserOptions, headless: false }); const context = await browser.newContext({ viewport: null }); diff --git a/test/keyboard.spec.ts b/test/keyboard.spec.ts index 9ecb85caa8..e27d902261 100644 --- a/test/keyboard.spec.ts +++ b/test/keyboard.spec.ts @@ -82,7 +82,9 @@ it('insertText should only emit input event', async ({page, server}) => { expect(await events.jsonValue()).toEqual(['input']); }); -it.fail(options.FIREFOX && MAC)('should report shiftKey', async ({page, server}) => { +it('should report shiftKey', test => { + test.fail(options.FIREFOX && MAC); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/input/keyboard.html'); const keyboard = page.keyboard; const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17}; @@ -339,7 +341,9 @@ it('should be able to prevent selectAll', async ({page, server}) => { expect(await page.$eval('textarea', textarea => textarea.value)).toBe('some tex'); }); -it.skip(!MAC)('should support MacOS shortcuts', async ({page, server}) => { +it('should support MacOS shortcuts', test => { + test.skip(!MAC); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/input/textarea.html'); const textarea = await page.$('textarea'); await textarea.type('some text'); @@ -379,7 +383,9 @@ it('should work after a cross origin navigation', async ({page, server}) => { }); // event.keyIdentifier has been removed from all browsers except WebKit -it.skip(!options.WEBKIT)('should expose keyIdentifier in webkit', async ({page, server}) => { +it('should expose keyIdentifier in webkit', test => { + test.skip(!options.WEBKIT); +}, async ({page, server}) => { const lastEvent = await captureLastKeydown(page); const keyMap = { 'ArrowUp': 'Up', diff --git a/test/mouse.spec.ts b/test/mouse.spec.ts index fcd7e28a64..1b91c70f4a 100644 --- a/test/mouse.spec.ts +++ b/test/mouse.spec.ts @@ -27,7 +27,9 @@ function dimensions() { }; } -it.flaky(options.FIREFOX && WIN)('should click the document', async ({page, server}) => { +it('should click the document', test => { + test.flaky(options.FIREFOX && WIN); +}, async ({page, server}) => { // Occasionally times out on options.FIREFOX on Windows: https://github.com/microsoft/playwright/pull/1911/checks?check_run_id=607149016 await page.evaluate(() => { window['clickPromise'] = new Promise(resolve => { @@ -164,7 +166,9 @@ it('should tween mouse movement', async ({page}) => { ]); }); -it.skip(options.FIREFOX)('should work with mobile viewports and cross process navigations', async ({browser, server}) => { +it('should work with mobile viewports and cross process navigations', test => { + test.skip(options.FIREFOX); +}, async ({browser, server}) => { // @see https://crbug.com/929806 const context = await browser.newContext({ viewport: {width: 360, height: 640}, isMobile: true }); const page = await context.newPage(); diff --git a/test/network-request.spec.ts b/test/network-request.spec.ts index 967750f595..e0952c972c 100644 --- a/test/network-request.spec.ts +++ b/test/network-request.spec.ts @@ -55,7 +55,9 @@ it('should return headers', async ({page, server}) => { expect(response.request().headers()['user-agent']).toContain('WebKit'); }); -it.fail(options.CHROMIUM || options.WEBKIT)('should get the same headers as the server', async ({page, server}) => { +it('should get the same headers as the server', test => { + test.fail(options.CHROMIUM || options.WEBKIT); +}, async ({page, server}) => { await page.goto(server.PREFIX + '/empty.html'); let serverRequest; server.setRoute('/something', (request, response) => { diff --git a/test/page-add-script-tag.spec.ts b/test/page-add-script-tag.spec.ts index 78e00387d0..ec4e773cda 100644 --- a/test/page-add-script-tag.spec.ts +++ b/test/page-add-script-tag.spec.ts @@ -74,7 +74,9 @@ it('should work with a path', async ({page, server}) => { expect(await page.evaluate(() => window['__injected'])).toBe(42); }); -it.skip(options.WEBKIT)('should include sourceURL when path is provided', async ({page, server}) => { +it('should include sourceURL when path is provided', test => { + test.skip(options.WEBKIT); +}, async ({page, server}) => { await page.goto(server.EMPTY_PAGE); await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); const result = await page.evaluate(() => window['__injectedError'].stack); diff --git a/test/page-basic.spec.ts b/test/page-basic.spec.ts index e25b9cbd0b..f8ea2bfb76 100644 --- a/test/page-basic.spec.ts +++ b/test/page-basic.spec.ts @@ -244,7 +244,9 @@ it('frame.press should work', async ({page, server}) => { expect(await frame.evaluate(() => document.querySelector('textarea').value)).toBe('a'); }); -it.fail(options.FIREFOX)('frame.focus should work multiple times', async ({ context, server }) => { +it('frame.focus should work multiple times', test => { + test.fail(options.FIREFOX); +}, async ({ context, server }) => { const page1 = await context.newPage(); const page2 = await context.newPage(); for (const page of [page1, page2]) { diff --git a/test/page-evaluate.spec.ts b/test/page-evaluate.spec.ts index 820701fb97..8ccd4b75f7 100644 --- a/test/page-evaluate.spec.ts +++ b/test/page-evaluate.spec.ts @@ -415,7 +415,9 @@ it('should not throw an error when evaluation does a navigation', async ({ page, expect(result).toEqual([42]); }); -it.fixme(options.WEBKIT)('should not throw an error when evaluation does a synchronous navigation and returns an object', async ({ page, server }) => { +it('should not throw an error when evaluation does a synchronous navigation and returns an object', test => { + test.fixme(options.WEBKIT); +}, async ({ page, server }) => { // It is imporant to be on about:blank for sync reload. const result = await page.evaluate(() => { window.location.reload(); diff --git a/test/page-event-crash.spec.ts b/test/page-event-crash.spec.ts index e9d458ad1e..648d505d3d 100644 --- a/test/page-event-crash.spec.ts +++ b/test/page-event-crash.spec.ts @@ -26,42 +26,47 @@ function crash(pageImpl, browserName) { pageImpl._delegate._session.send('Page.crash', {}).catch(e => {}); } -it.fail(options.WIRE).flaky(options.FIREFOX && WIN)('should emit crash event when page crashes', async ({page, browserName, toImpl}) => { - await page.setContent(`
This page should crash
`); - crash(toImpl(page), browserName); - await new Promise(f => page.on('crash', f)); -}); +describe.fixme(options.WIRE).flaky(options.FIREFOX && WIN)('', () => { + it('should emit crash event when page crashes', async ({page, browserName, toImpl}) => { + await page.setContent(`
This page should crash
`); + crash(toImpl(page), browserName); + await new Promise(f => page.on('crash', f)); + }); -it.fail(options.WIRE).flaky(options.FIREFOX && WIN)('should throw on any action after page crashes', async ({page, browserName, toImpl}) => { - await page.setContent(`
This page should crash
`); - crash(toImpl(page), browserName); - await page.waitForEvent('crash'); - const err = await page.evaluate(() => {}).then(() => null, e => e); - expect(err).toBeTruthy(); - expect(err.message).toContain('crash'); -}); + it('should throw on any action after page crashes', async ({page, browserName, toImpl}) => { + await page.setContent(`
This page should crash
`); + crash(toImpl(page), browserName); + await page.waitForEvent('crash'); + const err = await page.evaluate(() => {}).then(() => null, e => e); + expect(err).toBeTruthy(); + expect(err.message).toContain('crash'); + }); -it.fail(options.WIRE).flaky(options.FIREFOX && WIN)('should cancel waitForEvent when page crashes', async ({page, browserName, toImpl}) => { - await page.setContent(`
This page should crash
`); - const promise = page.waitForEvent('response').catch(e => e); - crash(toImpl(page), browserName); - const error = await promise; - expect(error.message).toContain('Page crashed'); -}); + it('should cancel waitForEvent when page crashes', async ({page, browserName, toImpl}) => { + await page.setContent(`
This page should crash
`); + const promise = page.waitForEvent('response').catch(e => e); + crash(toImpl(page), browserName); + const error = await promise; + expect(error.message).toContain('Page crashed'); + }); -it.fixme(options.WIRE).flaky(options.FIREFOX && WIN)('should cancel navigation when page crashes', async ({page, browserName, toImpl, server}) => { - await page.setContent(`
This page should crash
`); - server.setRoute('/one-style.css', () => {}); - const promise = page.goto(server.PREFIX + '/one-style.html').catch(e => e); - await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); - crash(toImpl(page), browserName); - const error = await promise; - expect(error.message).toContain('Navigation failed because page crashed'); -}); + it('should cancel navigation when page crashes', async ({page, browserName, toImpl, server}) => { + await page.setContent(`
This page should crash
`); + server.setRoute('/one-style.css', () => {}); + const promise = page.goto(server.PREFIX + '/one-style.html').catch(e => e); + await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); + crash(toImpl(page), browserName); + const error = await promise; + expect(error.message).toContain('Navigation failed because page crashed'); + }); -it.fixme(options.WIRE).flaky(options.FIREFOX && WIN)('should be able to close context when page crashes', async ({page, browserName, toImpl}) => { - await page.setContent(`
This page should crash
`); - crash(toImpl(page), browserName); - await page.waitForEvent('crash'); - await page.context().close(); + it('should be able to close context when page crashes', test => { + test.fixme(options.WIRE); + test.flaky(options.FIREFOX && WIN); + }, async ({page, browserName, toImpl}) => { + await page.setContent(`
This page should crash
`); + crash(toImpl(page), browserName); + await page.waitForEvent('crash'); + await page.context().close(); + }); }); diff --git a/test/page-event-pageerror.spec.ts b/test/page-event-pageerror.spec.ts index 1770cc6333..d17bb81315 100644 --- a/test/page-event-pageerror.spec.ts +++ b/test/page-event-pageerror.spec.ts @@ -31,7 +31,9 @@ it('should fire', async ({page, server}) => { expect(error.stack).toBe(stack); }); -it.fail(options.WEBKIT)('should contain sourceURL', async ({page, server}) => { +it('should contain sourceURL', test => { + test.fail(options.WEBKIT); +}, async ({page, server}) => { const [error] = await Promise.all([ page.waitForEvent('pageerror'), page.goto(server.PREFIX + '/error.html'), @@ -55,7 +57,9 @@ it('should handle odd values', async ({page}) => { } }); -it.fail(options.FIREFOX)('should handle object', async ({page}) => { +it('should handle object', test => { + test.fail(options.FIREFOX); +}, async ({page}) => { // Firefox just does not report this error. const [error] = await Promise.all([ page.waitForEvent('pageerror'), @@ -64,7 +68,9 @@ it.fail(options.FIREFOX)('should handle object', async ({page}) => { expect(error.message).toBe(options.CHROMIUM ? 'Object' : '[object Object]'); }); -it.fail(options.FIREFOX)('should handle window', async ({page}) => { +it('should handle window', test => { + test.fail(options.FIREFOX); +}, async ({page}) => { // Firefox just does not report this error. const [error] = await Promise.all([ page.waitForEvent('pageerror'), diff --git a/test/page-fill.spec.ts b/test/page-fill.spec.ts index 0cc68c6422..24bd4af678 100644 --- a/test/page-fill.spec.ts +++ b/test/page-fill.spec.ts @@ -60,7 +60,9 @@ it('should fill date input after clicking', async ({page, server}) => { expect(await page.$eval('input', input => input.value)).toBe('2020-03-02'); }); -it.skip(options.WEBKIT)('should throw on incorrect date', async ({page, server}) => { +it('should throw on incorrect date', test => { + test.skip(options.WEBKIT); +}, async ({page}) => { await page.setContent(''); const error = await page.fill('input', '2020-13-05').catch(e => e); expect(error.message).toContain('Malformed value'); @@ -72,7 +74,9 @@ it('should fill time input', async ({page, server}) => { expect(await page.$eval('input', input => input.value)).toBe('13:15'); }); -it.skip(options.WEBKIT)('should throw on incorrect time', async ({page, server}) => { +it('should throw on incorrect time', test => { + test.skip(options.WEBKIT); +}, async ({page}) => { await page.setContent(''); const error = await page.fill('input', '25:05').catch(e => e); expect(error.message).toContain('Malformed value'); @@ -84,7 +88,9 @@ it('should fill datetime-local input', async ({page, server}) => { expect(await page.$eval('input', input => input.value)).toBe('2020-03-02T05:15'); }); -it.skip(options.WEBKIT || options.FIREFOX)('should throw on incorrect datetime-local', async ({page, server}) => { +it('should throw on incorrect datetime-local', test => { + test.skip(options.WEBKIT || options.FIREFOX); +}, async ({page, server}) => { await page.setContent(''); const error = await page.fill('input', 'abc').catch(e => e); expect(error.message).toContain('Malformed value'); diff --git a/test/page-goto.spec.ts b/test/page-goto.spec.ts index 1f7a927998..dab8b9d207 100644 --- a/test/page-goto.spec.ts +++ b/test/page-goto.spec.ts @@ -474,9 +474,10 @@ it('should fail when canceled by another navigation', async ({page, server}) => expect(error.message).toBeTruthy(); }); -it.skip(true)('extraHttpHeaders should be pushed to provisional page', async ({page, server}) => { - // This test is flaky, because we cannot await page.setExtraHTTPHeaders. +it('extraHttpHeaders should be pushed to provisional page', test => { + test.flaky('This test is flaky, because we cannot await page.setExtraHTTPHeaders.'); // We need a way to test our implementation by more than just public api. +}, async ({page, server}) => { await page.goto(server.EMPTY_PAGE); const pagePath = '/one-style.html'; server.setRoute(pagePath, async (req, res) => { diff --git a/test/page-history.spec.ts b/test/page-history.spec.ts index 2d8cbe4796..be08dc6041 100644 --- a/test/page-history.spec.ts +++ b/test/page-history.spec.ts @@ -52,7 +52,9 @@ it('page.goBack should work with HistoryAPI', async ({page, server}) => { expect(page.url()).toBe(server.PREFIX + '/first.html'); }); -it.fail(options.WEBKIT && MAC)('page.goBack should work for file urls', async ({page, server, asset}) => { +it('page.goBack should work for file urls', test => { + test.fail(options.WEBKIT && MAC); +}, async ({page, server, asset}) => { // WebKit embedder fails to go back/forward to the file url. const url1 = url.pathToFileURL(asset('empty.html')).href; const url2 = server.EMPTY_PAGE; diff --git a/test/page-screenshot.spec.ts b/test/page-screenshot.spec.ts index 1f7338caaf..bd37cab3fd 100644 --- a/test/page-screenshot.spec.ts +++ b/test/page-screenshot.spec.ts @@ -23,258 +23,271 @@ import fs from 'fs'; // Firefox headful produces a different image. const ffheadful = options.FIREFOX && !options.HEADLESS; -it.skip(ffheadful)('should work', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-sanity.png')); -}); - -it.skip(ffheadful)('should clip rect', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - clip: { - x: 50, - y: 100, - width: 150, - height: 100 - } +describe.skip(ffheadful)('page screenshot', () => { + it('should work', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-sanity.png')); }); - expect(screenshot).toMatchImage(golden('screenshot-clip-rect.png')); -}); -it.skip(ffheadful)('should clip rect with fullPage', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - await page.evaluate(() => window.scrollBy(150, 200)); - const screenshot = await page.screenshot({ - fullPage: true, - clip: { - x: 50, - y: 100, - width: 150, - height: 100, - }, - }); - expect(screenshot).toMatchImage(golden('screenshot-clip-rect.png')); -}); - -it.skip(ffheadful)('should clip elements to the viewport', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - clip: { - x: 50, - y: 450, - width: 1000, - height: 100 - } - }); - expect(screenshot).toMatchImage(golden('screenshot-offscreen-clip.png')); -}); - -it.skip(ffheadful)('should throw on clip outside the viewport', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshotError = await page.screenshot({ - clip: { - x: 50, - y: 650, - width: 100, - height: 100 - } - }).catch(error => error); - expect(screenshotError.message).toContain('Clipped area is either empty or outside the resulting image'); -}); - -it.skip(ffheadful)('should run in parallel', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const promises = []; - for (let i = 0; i < 3; ++i) { - promises.push(page.screenshot({ + it('should clip rect', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const screenshot = await page.screenshot({ clip: { - x: 50 * i, - y: 0, - width: 50, - height: 50 + x: 50, + y: 100, + width: 150, + height: 100 } - })); - } - const screenshots = await Promise.all(promises); - expect(screenshots[1]).toMatchImage(golden('grid-cell-1.png')); -}); - -it.skip(ffheadful)('should take fullPage screenshots', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ - fullPage: true + }); + expect(screenshot).toMatchImage(golden('screenshot-clip-rect.png')); }); - expect(screenshot).toMatchImage(golden('screenshot-grid-fullpage.png')); -}); -it.skip(ffheadful)('should restore viewport after fullPage screenshot', async ({page, server}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot({ fullPage: true }); - expect(screenshot).toBeInstanceOf(Buffer); - await utils.verifyViewport(page, 500, 500); -}); + it('should clip rect with fullPage', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + await page.evaluate(() => window.scrollBy(150, 200)); + const screenshot = await page.screenshot({ + fullPage: true, + clip: { + x: 50, + y: 100, + width: 150, + height: 100, + }, + }); + expect(screenshot).toMatchImage(golden('screenshot-clip-rect.png')); + }); -it.skip(ffheadful)('should run in parallel in multiple pages', async ({server, context, golden}) => { - const N = 5; - const pages = await Promise.all(Array(N).fill(0).map(async () => { + it('should clip elements to the viewport', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const screenshot = await page.screenshot({ + clip: { + x: 50, + y: 450, + width: 1000, + height: 100 + } + }); + expect(screenshot).toMatchImage(golden('screenshot-offscreen-clip.png')); + }); + + it('should throw on clip outside the viewport', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const screenshotError = await page.screenshot({ + clip: { + x: 50, + y: 650, + width: 100, + height: 100 + } + }).catch(error => error); + expect(screenshotError.message).toContain('Clipped area is either empty or outside the resulting image'); + }); + + it('should run in parallel', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const promises = []; + for (let i = 0; i < 3; ++i) { + promises.push(page.screenshot({ + clip: { + x: 50 * i, + y: 0, + width: 50, + height: 50 + } + })); + } + const screenshots = await Promise.all(promises); + expect(screenshots[1]).toMatchImage(golden('grid-cell-1.png')); + }); + + it('should take fullPage screenshots', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const screenshot = await page.screenshot({ + fullPage: true + }); + expect(screenshot).toMatchImage(golden('screenshot-grid-fullpage.png')); + }); + + it('should restore viewport after fullPage screenshot', async ({page, server}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const screenshot = await page.screenshot({ fullPage: true }); + expect(screenshot).toBeInstanceOf(Buffer); + await utils.verifyViewport(page, 500, 500); + }); + + it('should run in parallel in multiple pages', async ({server, context, golden}) => { + const N = 5; + const pages = await Promise.all(Array(N).fill(0).map(async () => { + const page = await context.newPage(); + await page.goto(server.PREFIX + '/grid.html'); + return page; + })); + const promises = []; + for (let i = 0; i < N; ++i) + promises.push(pages[i].screenshot({ clip: { x: 50 * (i % 2), y: 0, width: 50, height: 50 } })); + const screenshots = await Promise.all(promises); + for (let i = 0; i < N; ++i) + expect(screenshots[i]).toMatchImage(golden(`grid-cell-${i % 2}.png`)); + await Promise.all(pages.map(page => page.close())); + }); + + it('should allow transparency', test => { + test.fail(options.FIREFOX); + }, async ({page, golden}) => { + await page.setViewportSize({ width: 50, height: 150 }); + await page.setContent(` + +
+
+
+ `); + const screenshot = await page.screenshot({omitBackground: true}); + expect(screenshot).toMatchImage(golden('transparent.png')); + }); + + it('should render white background on jpeg file', async ({page, server, golden}) => { + await page.setViewportSize({ width: 100, height: 100 }); + await page.goto(server.EMPTY_PAGE); + const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'}); + expect(screenshot).toMatchImage(golden('white.jpg')); + }); + + it('should work with odd clip size on Retina displays', async ({page, golden}) => { + const screenshot = await page.screenshot({ + clip: { + x: 0, + y: 0, + width: 11, + height: 11, + } + }); + expect(screenshot).toMatchImage(golden('screenshot-clip-odd-size.png')); + }); + + it('should work with a mobile viewport', test => { + test.skip(options.FIREFOX); + }, async ({browser, server, golden}) => { + const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/overflow.html'); + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-mobile.png')); + await context.close(); + }); + + it('should work with a mobile viewport and clip', test => { + test.skip(options.FIREFOX); + }, async ({browser, server, golden}) => { + const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/overflow.html'); + const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } }); + expect(screenshot).toMatchImage(golden('screenshot-mobile-clip.png')); + await context.close(); + }); + + it('should work with a mobile viewport and fullPage', test => { + test.skip(options.FIREFOX); + }, async ({browser, server, golden}) => { + const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/overflow-large.html'); + const screenshot = await page.screenshot({ fullPage: true }); + expect(screenshot).toMatchImage(golden('screenshot-mobile-fullpage.png')); + await context.close(); + }); + + it('should work for canvas', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/screenshots/canvas.html'); + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-canvas.png'), { threshold: 0.3 }); + }); + + it('should work for webgl', test => { + test.fixme(options.FIREFOX); + test.fixme(options.WEBKIT && LINUX); + }, async ({page, server, golden}) => { + await page.setViewportSize({width: 640, height: 480}); + await page.goto(server.PREFIX + '/screenshots/webgl.html'); + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-webgl.png')); + }); + + it('should work for translateZ', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/screenshots/translateZ.html'); + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-translateZ.png')); + }); + + it('should work while navigating', async ({page, server}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/redirectloop1.html'); + for (let i = 0; i < 10; i++) { + const screenshot = await page.screenshot({ fullPage: true }).catch(e => { + if (e.message.includes('Cannot take a screenshot while page is navigating')) + return Buffer.from(''); + throw e; + }); + expect(screenshot).toBeInstanceOf(Buffer); + } + }); + + it('should work with device scale factor', async ({browser, server, golden}) => { + const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 }); const page = await context.newPage(); await page.goto(server.PREFIX + '/grid.html'); - return page; - })); - const promises = []; - for (let i = 0; i < N; ++i) - promises.push(pages[i].screenshot({ clip: { x: 50 * (i % 2), y: 0, width: 50, height: 50 } })); - const screenshots = await Promise.all(promises); - for (let i = 0; i < N; ++i) - expect(screenshots[i]).toMatchImage(golden(`grid-cell-${i % 2}.png`)); - await Promise.all(pages.map(page => page.close())); -}); - -it.fail(options.FIREFOX)('should allow transparency', async ({page, golden}) => { - await page.setViewportSize({ width: 50, height: 150 }); - await page.setContent(` - -
-
-
- `); - const screenshot = await page.screenshot({omitBackground: true}); - expect(screenshot).toMatchImage(golden('transparent.png')); -}); - -it.skip(ffheadful)('should render white background on jpeg file', async ({page, server, golden}) => { - await page.setViewportSize({ width: 100, height: 100 }); - await page.goto(server.EMPTY_PAGE); - const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'}); - expect(screenshot).toMatchImage(golden('white.jpg')); -}); - -it.skip(ffheadful)('should work with odd clip size on Retina displays', async ({page, golden}) => { - const screenshot = await page.screenshot({ - clip: { - x: 0, - y: 0, - width: 11, - height: 11, - } + const screenshot = await page.screenshot(); + expect(screenshot).toMatchImage(golden('screenshot-device-scale-factor.png')); + await context.close(); }); - expect(screenshot).toMatchImage(golden('screenshot-clip-odd-size.png')); -}); -it.skip(options.FIREFOX)('should work with a mobile viewport', async ({browser, server, golden}) => { - const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/overflow.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-mobile.png')); - await context.close(); -}); + it('should work with iframe in shadow', async ({page, server, golden}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html'); + expect(await page.screenshot()).toMatchImage(golden('screenshot-iframe.png')); + }); -it.skip(options.FIREFOX)('should work with a mobile viewport and clip', async ({browser, server, golden}) => { - const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/overflow.html'); - const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } }); - expect(screenshot).toMatchImage(golden('screenshot-mobile-clip.png')); - await context.close(); -}); + it('path option should work', async ({page, server, golden, tmpDir}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const outputPath = path.join(tmpDir, 'screenshot.png'); + await page.screenshot({path: outputPath}); + expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png')); + }); -it.skip(options.FIREFOX)('should work with a mobile viewport and fullPage', async ({browser, server, golden}) => { - const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/overflow-large.html'); - const screenshot = await page.screenshot({ fullPage: true }); - expect(screenshot).toMatchImage(golden('screenshot-mobile-fullpage.png')); - await context.close(); -}); + it('path option should create subdirectories', async ({page, server, golden, tmpDir}) => { + await page.setViewportSize({width: 500, height: 500}); + await page.goto(server.PREFIX + '/grid.html'); + const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png'); + await page.screenshot({path: outputPath}); + expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png')); + }); -it.skip(ffheadful)('should work for canvas', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/screenshots/canvas.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-canvas.png'), { threshold: 0.3 }); -}); + it('path option should detect jpeg', async ({page, server, golden, tmpDir}) => { + await page.setViewportSize({ width: 100, height: 100 }); + await page.goto(server.EMPTY_PAGE); + const outputPath = path.join(tmpDir, 'screenshot.jpg'); + const screenshot = await page.screenshot({omitBackground: true, path: outputPath}); + expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('white.jpg')); + expect(screenshot).toMatchImage(golden('white.jpg')); + }); -it.skip(ffheadful)('should work for translateZ', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/screenshots/translateZ.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-translateZ.png')); -}); - -it.fixme(options.FIREFOX || options.WEBKIT && LINUX)('should work for webgl', async ({page, server, golden}) => { - await page.setViewportSize({width: 640, height: 480}); - await page.goto(server.PREFIX + '/screenshots/webgl.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-webgl.png')); -}); - -it.skip(ffheadful)('should work while navigating', async ({page, server}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/redirectloop1.html'); - for (let i = 0; i < 10; i++) { - const screenshot = await page.screenshot({ fullPage: true }).catch(e => { - if (e.message.includes('Cannot take a screenshot while page is navigating')) - return Buffer.from(''); - throw e; - }); - expect(screenshot).toBeInstanceOf(Buffer); - } -}); - -it.skip(ffheadful)('should work with device scale factor', async ({browser, server, golden}) => { - const context = await browser.newContext({ viewport: { width: 320, height: 480 }, deviceScaleFactor: 2 }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/grid.html'); - const screenshot = await page.screenshot(); - expect(screenshot).toMatchImage(golden('screenshot-device-scale-factor.png')); - await context.close(); -}); - -it.skip(ffheadful)('should work with iframe in shadow', async ({page, server, golden}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html'); - expect(await page.screenshot()).toMatchImage(golden('screenshot-iframe.png')); -}); - -it.skip(ffheadful)('path option should work', async ({page, server, golden, tmpDir}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const outputPath = path.join(tmpDir, 'screenshot.png'); - await page.screenshot({path: outputPath}); - expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png')); -}); - -it.skip(ffheadful)('path option should create subdirectories', async ({page, server, golden, tmpDir}) => { - await page.setViewportSize({width: 500, height: 500}); - await page.goto(server.PREFIX + '/grid.html'); - const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png'); - await page.screenshot({path: outputPath}); - expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png')); -}); - -it.skip(ffheadful)('path option should detect jpeg', async ({page, server, golden, tmpDir}) => { - await page.setViewportSize({ width: 100, height: 100 }); - await page.goto(server.EMPTY_PAGE); - const outputPath = path.join(tmpDir, 'screenshot.jpg'); - const screenshot = await page.screenshot({omitBackground: true, path: outputPath}); - expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('white.jpg')); - expect(screenshot).toMatchImage(golden('white.jpg')); -}); - -it.skip(ffheadful)('path option should throw for unsupported mime type', async ({page, server, golden, tmpDir}) => { - const error = await page.screenshot({ path: 'file.txt' }).catch(e => e); - expect(error.message).toContain('path: unsupported mime type "text/plain"'); + it('path option should throw for unsupported mime type', async ({page, server, golden, tmpDir}) => { + const error = await page.screenshot({ path: 'file.txt' }).catch(e => e); + expect(error.message).toContain('path: unsupported mime type "text/plain"'); + }); }); diff --git a/test/pdf.spec.ts b/test/pdf.spec.ts index 4d6194f306..6a19708d99 100644 --- a/test/pdf.spec.ts +++ b/test/pdf.spec.ts @@ -20,14 +20,17 @@ import fs from 'fs'; import path from 'path'; -// Printing to pdf is currently only supported in headless chromium. -it.skip(!(options.HEADLESS && options.CHROMIUM))('should be able to save file', async ({page, tmpDir}) => { +it('should be able to save file', test => { + test.skip(!(options.HEADLESS && options.CHROMIUM), 'Printing to pdf is currently only supported in headless chromium.'); +}, async ({page, tmpDir}) => { const outputFile = path.join(tmpDir, 'output.pdf'); await page.pdf({path: outputFile}); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); fs.unlinkSync(outputFile); }); -it.skip(options.CHROMIUM)('should be able to save file', async ({page}) => { +it('should only have pdf in chromium', test => { + test.skip(options.CHROMIUM); +}, async ({page}) => { expect(page.pdf).toBe(undefined); }); diff --git a/test/permissions.spec.ts b/test/permissions.spec.ts index b8a4ba72d1..8f06d2ec08 100644 --- a/test/permissions.spec.ts +++ b/test/permissions.spec.ts @@ -98,7 +98,11 @@ describe.skip(options.WEBKIT)('permissions', () => { expect(await getPermission(page, 'geolocation')).toBe('prompt'); }); - it.fail(options.WEBKIT).fail(options.CHROMIUM && !options.HEADLESS).flaky(options.FIREFOX && LINUX)('should trigger permission onchange', async ({page, server, context}) => { + it('should trigger permission onchange', test => { + test.fail(options.WEBKIT); + test.fail(options.CHROMIUM && !options.HEADLESS); + test.flaky(options.FIREFOX && LINUX); + }, async ({page, server, context}) => { // TODO: flaky // - Linux: https://github.com/microsoft/playwright/pull/1790/checks?check_run_id=587327883 // - Win: https://ci.appveyor.com/project/aslushnikov/playwright/builds/32402536 @@ -140,8 +144,11 @@ describe.skip(options.WEBKIT)('permissions', () => { await otherContext.close(); }); - it.fail(options.WEBKIT || options.FIREFOX || (options.CHROMIUM && !options.HEADLESS))('should support clipboard read', async ({page, server, context, browser}) => { - // No such permissions (requires flag) in Firefox + it('should support clipboard read', test => { + test.fail(options.WEBKIT); + test.fail(options.FIREFOX, 'No such permissions (requires flag) in Firefox'); + test.fail(options.CHROMIUM && !options.HEADLESS); + }, async ({page, server, context}) => { await page.goto(server.EMPTY_PAGE); expect(await getPermission(page, 'clipboard-read')).toBe('prompt'); let error; diff --git a/test/proxy.spec.ts b/test/proxy.spec.ts index 5b40515ef6..150aebfafb 100644 --- a/test/proxy.spec.ts +++ b/test/proxy.spec.ts @@ -63,8 +63,9 @@ it('should authenticate', async ({browserType, defaultBrowserOptions, server}) = await browser.close(); }); -it.fail(options.CHROMIUM && !options.HEADLESS)('should exclude patterns', async ({browserType, defaultBrowserOptions, server}) => { - // Chromium headful crashes with CHECK(!in_frame_tree_) in RenderFrameImpl::OnDeleteFrame. +it('should exclude patterns', test => { + test.fail(options.CHROMIUM && !options.HEADLESS, 'Chromium headful crashes with CHECK(!in_frame_tree_) in RenderFrameImpl::OnDeleteFrame.'); +}, async ({browserType, defaultBrowserOptions, server}) => { server.setRoute('/target.html', async (req, res) => { res.end('Served by the proxy'); }); diff --git a/test/request-fulfill.spec.ts b/test/request-fulfill.spec.ts index 7da4e4a933..9398fcf7df 100644 --- a/test/request-fulfill.spec.ts +++ b/test/request-fulfill.spec.ts @@ -50,8 +50,9 @@ it('should work with status code 422', async ({page, server}) => { expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); }); -it.skip(options.FIREFOX && !options.HEADLESS)('should allow mocking binary responses', async ({page, server, golden}) => { - // Firefox headful produces a different image. +it('should allow mocking binary responses', test => { + test.skip(options.FIREFOX && !options.HEADLESS, '// Firefox headful produces a different image.'); +}, async ({page, server, golden}) => { await page.route('**/*', route => { const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); route.fulfill({ @@ -69,7 +70,9 @@ it.skip(options.FIREFOX && !options.HEADLESS)('should allow mocking binary respo expect(await img.screenshot()).toMatchImage(golden('mock-binary-response.png')); }); -it.skip(options.FIREFOX && !options.HEADLESS)('should allow mocking svg with charset', async ({page, server, golden}) => { +it('should allow mocking svg with charset', test => { + test.skip(options.FIREFOX && !options.HEADLESS, '// Firefox headful produces a different image.'); +}, async ({page, server, golden}) => { // Firefox headful produces a different image. await page.route('**/*', route => { route.fulfill({ diff --git a/test/screencast.spec.ts b/test/screencast.spec.ts index f667c3bb30..bfc0e1d464 100644 --- a/test/screencast.spec.ts +++ b/test/screencast.spec.ts @@ -172,132 +172,132 @@ class VideoPlayer { } } -it.fixme(options.CHROMIUM).flaky(options.FIREFOX && MAC)('should capture static page', async ({page, tmpDir, videoPlayer, toImpl}) => { - if (!toImpl) - return; - const videoFile = path.join(tmpDir, 'v.webm'); - await page.evaluate(() => document.body.style.backgroundColor = 'red'); - await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); - // TODO: in WebKit figure out why video size is not reported correctly for - // static pictures. - if (options.HEADLESS && options.WEBKIT) - await page.setViewportSize({width: 1270, height: 950}); - await new Promise(r => setTimeout(r, 300)); - await toImpl(page)._delegate.stopScreencast(); - expect(fs.existsSync(videoFile)).toBe(true); +describe.skip(options.WIRE).fixme(options.CHROMIUM)('screencast', () => { + it('should capture static page', test => { + test.fixme(); + }, async ({page, tmpDir, videoPlayer, toImpl}) => { + const videoFile = path.join(tmpDir, 'v.webm'); + await page.evaluate(() => document.body.style.backgroundColor = 'red'); + await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); + // TODO: in WebKit figure out why video size is not reported correctly for + // static pictures. + if (options.HEADLESS && options.WEBKIT) + await page.setViewportSize({width: 1270, height: 950}); + await new Promise(r => setTimeout(r, 300)); + await toImpl(page)._delegate.stopScreencast(); + expect(fs.existsSync(videoFile)).toBe(true); - await videoPlayer.load(videoFile); - const duration = await videoPlayer.duration(); - expect(duration).toBeGreaterThan(0); + await videoPlayer.load(videoFile); + const duration = await videoPlayer.duration(); + expect(duration).toBeGreaterThan(0); - expect(await videoPlayer.videoWidth()).toBe(640); - expect(await videoPlayer.videoHeight()).toBe(480); + expect(await videoPlayer.videoWidth()).toBe(640); + expect(await videoPlayer.videoHeight()).toBe(480); - await videoPlayer.seekLastNonEmptyFrame(); - const pixels = await videoPlayer.pixels(); - expectAll(pixels, almostRed); -}); - -it.fixme(options.CHROMIUM).flaky(options.WEBKIT)('should capture navigation', async ({page, tmpDir, server, videoPlayer, toImpl}) => { - if (!toImpl) - return; - const videoFile = path.join(tmpDir, 'v.webm'); - await page.goto(server.PREFIX + '/background-color.html#rgb(0,0,0)'); - await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); - // TODO: in WebKit figure out why video size is not reported correctly for - // static pictures. - if (options.HEADLESS && options.WEBKIT) - await page.setViewportSize({width: 1270, height: 950}); - await new Promise(r => setTimeout(r, 300)); - await page.goto(server.CROSS_PROCESS_PREFIX + '/background-color.html#rgb(100,100,100)'); - await new Promise(r => setTimeout(r, 300)); - await toImpl(page)._delegate.stopScreencast(); - expect(fs.existsSync(videoFile)).toBe(true); - - await videoPlayer.load(videoFile); - const duration = await videoPlayer.duration(); - expect(duration).toBeGreaterThan(0); - - { - await videoPlayer.seekFirstNonEmptyFrame(); - const pixels = await videoPlayer.pixels(); - expectAll(pixels, almostBlack); - } - - { await videoPlayer.seekLastNonEmptyFrame(); const pixels = await videoPlayer.pixels(); - expectAll(pixels, almostGrey); - } -}); - -// Accelerated compositing is disabled in WebKit on Windows. -it.fixme(options.CHROMIUM || (options.WEBKIT && WIN)).flaky(options.WEBKIT && LINUX)('should capture css transformation', async ({page, tmpDir, server, videoPlayer, toImpl}) => { - if (!toImpl) - return; - const videoFile = path.join(tmpDir, 'v.webm'); - await page.goto(server.PREFIX + '/rotate-z.html'); - await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); - // TODO: in WebKit figure out why video size is not reported correctly for - // static pictures. - if (options.HEADLESS && options.WEBKIT) - await page.setViewportSize({width: 1270, height: 950}); - await new Promise(r => setTimeout(r, 300)); - await toImpl(page)._delegate.stopScreencast(); - expect(fs.existsSync(videoFile)).toBe(true); - - await videoPlayer.load(videoFile); - const duration = await videoPlayer.duration(); - expect(duration).toBeGreaterThan(0); - - { - await videoPlayer.seekLastNonEmptyFrame(); - const pixels = await videoPlayer.pixels({x: 95, y: 45}); expectAll(pixels, almostRed); - } -}); - -it.slow().fixme(options.CHROMIUM)('should fire start/stop events when page created/closed', async ({browser, tmpDir, server, toImpl}) => { - if (!toImpl) - return; - // Use server side of the context. All the code below also uses server side APIs. - const context = toImpl(await browser.newContext()); - await context._enableScreencast({width: 640, height: 480, dir: tmpDir}); - expect(context._screencastOptions).toBeTruthy(); - - const [screencast, newPage] = await Promise.all([ - new Promise(resolve => context.on('screencaststarted', resolve)) as Promise, - context.newPage(), - ]); - expect(screencast.page === newPage).toBe(true); - - const [videoFile] = await Promise.all([ - screencast.path(), - newPage.close(), - ]); - expect(path.dirname(videoFile)).toBe(tmpDir); - await context.close(); -}); - -it.fixme(options.CHROMIUM)('should fire start event for popups', async ({browser, tmpDir, server, toImpl}) => { - if (!toImpl) - return; - // Use server side of the context. All the code below also uses server side APIs. - const context = toImpl(await browser.newContext()); - await context._enableScreencast({width: 640, height: 480, dir: tmpDir}); - expect(context._screencastOptions).toBeTruthy(); - - const page = await context.newPage(); - await page.mainFrame().goto(server.EMPTY_PAGE); - const [screencast, popup] = await Promise.all([ - new Promise(resolve => context.on('screencaststarted', resolve)) as Promise, - new Promise(resolve => context.on('page', resolve)) as Promise, - page.mainFrame()._evaluateExpression(() => { - const win = window.open('about:blank'); - win.close(); - }, true) - ]); - expect(screencast.page === popup).toBe(true); - expect(path.dirname(await screencast.path())).toBe(tmpDir); - await context.close(); + }); + + it('should capture navigation', test => { + test.flaky(options.WEBKIT); + }, async ({page, tmpDir, server, videoPlayer, toImpl}) => { + const videoFile = path.join(tmpDir, 'v.webm'); + await page.goto(server.PREFIX + '/background-color.html#rgb(0,0,0)'); + await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); + // TODO: in WebKit figure out why video size is not reported correctly for + // static pictures. + if (options.HEADLESS && options.WEBKIT) + await page.setViewportSize({width: 1270, height: 950}); + await new Promise(r => setTimeout(r, 300)); + await page.goto(server.CROSS_PROCESS_PREFIX + '/background-color.html#rgb(100,100,100)'); + await new Promise(r => setTimeout(r, 300)); + await toImpl(page)._delegate.stopScreencast(); + expect(fs.existsSync(videoFile)).toBe(true); + + await videoPlayer.load(videoFile); + const duration = await videoPlayer.duration(); + expect(duration).toBeGreaterThan(0); + + { + await videoPlayer.seekFirstNonEmptyFrame(); + const pixels = await videoPlayer.pixels(); + expectAll(pixels, almostBlack); + } + + { + await videoPlayer.seekLastNonEmptyFrame(); + const pixels = await videoPlayer.pixels(); + expectAll(pixels, almostGrey); + } + }); + + it('should capture css transformation', test => { + test.fixme(options.WEBKIT && WIN, 'Accelerated compositing is disabled in WebKit on Windows.'); + test.flaky(options.WEBKIT && LINUX); + }, async ({page, tmpDir, server, videoPlayer, toImpl}) => { + const videoFile = path.join(tmpDir, 'v.webm'); + await page.goto(server.PREFIX + '/rotate-z.html'); + await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); + // TODO: in WebKit figure out why video size is not reported correctly for + // static pictures. + if (options.HEADLESS && options.WEBKIT) + await page.setViewportSize({width: 1270, height: 950}); + await new Promise(r => setTimeout(r, 300)); + await toImpl(page)._delegate.stopScreencast(); + expect(fs.existsSync(videoFile)).toBe(true); + + await videoPlayer.load(videoFile); + const duration = await videoPlayer.duration(); + expect(duration).toBeGreaterThan(0); + + { + await videoPlayer.seekLastNonEmptyFrame(); + const pixels = await videoPlayer.pixels({x: 95, y: 45}); + expectAll(pixels, almostRed); + } + }); + + it('should fire start/stop events when page created/closed', test => { + test.slow(); + }, async ({browser, tmpDir, server, toImpl}) => { + // Use server side of the context. All the code below also uses server side APIs. + const context = toImpl(await browser.newContext()); + await context._enableScreencast({width: 640, height: 480, dir: tmpDir}); + expect(context._screencastOptions).toBeTruthy(); + + const [screencast, newPage] = await Promise.all([ + new Promise(resolve => context.on('screencaststarted', resolve)) as Promise, + context.newPage(), + ]); + expect(screencast.page === newPage).toBe(true); + + const [videoFile] = await Promise.all([ + screencast.path(), + newPage.close(), + ]); + expect(path.dirname(videoFile)).toBe(tmpDir); + await context.close(); + }); + + it('should fire start event for popups', async ({browser, tmpDir, server, toImpl}) => { + // Use server side of the context. All the code below also uses server side APIs. + const context = toImpl(await browser.newContext()); + await context._enableScreencast({width: 640, height: 480, dir: tmpDir}); + expect(context._screencastOptions).toBeTruthy(); + + const page = await context.newPage(); + await page.mainFrame().goto(server.EMPTY_PAGE); + const [screencast, popup] = await Promise.all([ + new Promise(resolve => context.on('screencaststarted', resolve)) as Promise, + new Promise(resolve => context.on('page', resolve)) as Promise, + page.mainFrame()._evaluateExpression(() => { + const win = window.open('about:blank'); + win.close(); + }, true) + ]); + expect(screencast.page === popup).toBe(true); + expect(path.dirname(await screencast.path())).toBe(tmpDir); + await context.close(); + }); }); diff --git a/test/slowmo.spec.ts b/test/slowmo.spec.ts index 72ef37696d..dd20abf078 100644 --- a/test/slowmo.spec.ts +++ b/test/slowmo.spec.ts @@ -44,69 +44,172 @@ async function checkPageSlowMo(toImpl, page, task) { `); await checkSlowMo(toImpl, page, task); } - -it.skip(options.WIRE)('Page SlowMo $$eval', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.$$eval('button', () => void 0)); -}); -it.skip(options.WIRE)('Page SlowMo $eval', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.$eval('button', () => void 0)); -}); -it.skip(options.WIRE)('Page SlowMo check', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.check('.check')); -}); -it.skip(options.WIRE)('Page SlowMo click', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.click('button')); -}); -it.skip(options.WIRE)('Page SlowMo dblclick', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.dblclick('button')); -}); -it.skip(options.WIRE)('Page SlowMo dispatchEvent', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.dispatchEvent('button', 'click')); -}); -it.skip(options.WIRE)('Page SlowMo emulateMedia', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.emulateMedia({media: 'print'})); -}); -it.skip(options.WIRE)('Page SlowMo evaluate', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.evaluate(() => void 0)); -}); -it.skip(options.WIRE)('Page SlowMo evaluateHandle', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.evaluateHandle(() => window)); -}); -it.skip(options.WIRE)('Page SlowMo fill', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.fill('.fill', 'foo')); -}); -it.skip(options.WIRE)('Page SlowMo focus', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.focus('button')); -}); -it.skip(options.WIRE)('Page SlowMo goto', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.goto('about:blank')); -}); -it.skip(options.WIRE)('Page SlowMo hover', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.hover('button')); -}); -it.skip(options.WIRE)('Page SlowMo press', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.press('button', 'Enter')); -}); -it.skip(options.WIRE)('Page SlowMo reload', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.reload()); -}); -it.skip(options.WIRE)('Page SlowMo setContent', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.setContent('hello world')); -}); -it.skip(options.WIRE)('Page SlowMo selectOption', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.selectOption('select', 'foo')); -}); -it.skip(options.WIRE)('Page SlowMo setInputFiles', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.setInputFiles('.file', [])); -}); -it.skip(options.WIRE)('Page SlowMo setViewportSize', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.setViewportSize({height: 400, width: 400})); -}); -it.skip(options.WIRE)('Page SlowMo type', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.type('.fill', 'a')); -}); -it.skip(options.WIRE)('Page SlowMo uncheck', async ({page, toImpl}) => { - await checkPageSlowMo(toImpl, page, () => page.uncheck('.uncheck')); +describe.skip(options.WIRE)('slowMo', () => { + it('Page SlowMo $$eval', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.$$eval('button', () => void 0)); + }); + it('Page SlowMo $eval', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.$eval('button', () => void 0)); + }); + it('Page SlowMo check', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.check('.check')); + }); + it('Page SlowMo click', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.click('button')); + }); + it('Page SlowMo dblclick', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.dblclick('button')); + }); + it('Page SlowMo dispatchEvent', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.dispatchEvent('button', 'click')); + }); + it('Page SlowMo emulateMedia', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.emulateMedia({media: 'print'})); + }); + it('Page SlowMo evaluate', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.evaluate(() => void 0)); + }); + it('Page SlowMo evaluateHandle', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.evaluateHandle(() => window)); + }); + it('Page SlowMo fill', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.fill('.fill', 'foo')); + }); + it('Page SlowMo focus', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.focus('button')); + }); + it('Page SlowMo goto', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.goto('about:blank')); + }); + it('Page SlowMo hover', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.hover('button')); + }); + it('Page SlowMo press', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.press('button', 'Enter')); + }); + it('Page SlowMo reload', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.reload()); + }); + it('Page SlowMo setContent', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.setContent('hello world')); + }); + it('Page SlowMo selectOption', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.selectOption('select', 'foo')); + }); + it('Page SlowMo setInputFiles', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.setInputFiles('.file', [])); + }); + it('Page SlowMo setViewportSize', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.setViewportSize({height: 400, width: 400})); + }); + it('Page SlowMo type', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.type('.fill', 'a')); + }); + it('Page SlowMo uncheck', async ({page, toImpl}) => { + await checkPageSlowMo(toImpl, page, () => page.uncheck('.uncheck')); + }); + it('Frame SlowMo $$eval', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.$$eval('button', () => void 0)); + }); + it('Frame SlowMo $eval', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.$eval('button', () => void 0)); + }); + it('Frame SlowMo check', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.check('.check')); + }); + it('Frame SlowMo click', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.click('button')); + }); + it('Frame SlowMo dblclick', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.dblclick('button')); + }); + it('Frame SlowMo dispatchEvent', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.dispatchEvent('button', 'click')); + }); + it('Frame SlowMo evaluate', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.evaluate(() => void 0)); + }); + it('Frame SlowMo evaluateHandle', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.evaluateHandle(() => window)); + }); + it('Frame SlowMo fill', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.fill('.fill', 'foo')); + }); + it('Frame SlowMo focus', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.focus('button')); + }); + it('Frame SlowMo goto', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.goto('about:blank')); + }); + it('Frame SlowMo hover', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.hover('button')); + }); + it('Frame SlowMo press', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.press('button', 'Enter')); + }); + it('Frame SlowMo setContent', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.setContent('hello world')); + }); + it('Frame SlowMo selectOption', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.selectOption('select', 'foo')); + }); + it('Frame SlowMo setInputFiles', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.setInputFiles('.file', [])); + }); + it('Frame SlowMo type', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.type('.fill', 'a')); + }); + it('Frame SlowMo uncheck', async ({page, server, toImpl}) => { + await checkFrameSlowMo(toImpl, page, server, frame => frame.uncheck('.uncheck')); + }); + it('ElementHandle SlowMo $$eval', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'body', element => element.$$eval('button', () => void 0)); + }); + it('ElementHandle SlowMo $eval', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'body', element => element.$eval('button', () => void 0)); + }); + it('ElementHandle SlowMo check', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, '.check', element => element.check()); + }); + it('ElementHandle SlowMo click', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.click()); + }); + it('ElementHandle SlowMo dblclick', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.dblclick()); + }); + it('ElementHandle SlowMo dispatchEvent', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.dispatchEvent('click')); + }); + it('ElementHandle SlowMo evaluate', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.evaluate(() => void 0)); + }); + it('ElementHandle SlowMo evaluateHandle', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.evaluateHandle(() => void 0)); + }); + it('ElementHandle SlowMo fill', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, '.fill', element => element.fill('foo')); + }); + it('ElementHandle SlowMo focus', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.focus()); + }); + it('ElementHandle SlowMo hover', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.hover()); + }); + it('ElementHandle SlowMo press', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'button', element => element.press('Enter')); + }); + it('ElementHandle SlowMo selectOption', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, 'select', element => element.selectOption('foo')); + }); + it('ElementHandle SlowMo setInputFiles', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, '.file', element => element.setInputFiles([])); + }); + it('ElementHandle SlowMo type', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, '.fill', element => element.type('a')); + }); + it('ElementHandle SlowMo uncheck', async ({page, toImpl}) => { + await checkElementSlowMo(toImpl, page, '.uncheck', element => element.uncheck()); + }); }); async function checkFrameSlowMo(toImpl, page, server, task) { @@ -124,60 +227,6 @@ async function checkFrameSlowMo(toImpl, page, server, task) { await checkSlowMo(toImpl, page, task.bind(null, frame)); } -it.skip(options.WIRE)('Frame SlowMo $$eval', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.$$eval('button', () => void 0)); -}); -it.skip(options.WIRE)('Frame SlowMo $eval', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.$eval('button', () => void 0)); -}); -it.skip(options.WIRE)('Frame SlowMo check', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.check('.check')); -}); -it.skip(options.WIRE)('Frame SlowMo click', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.click('button')); -}); -it.skip(options.WIRE)('Frame SlowMo dblclick', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.dblclick('button')); -}); -it.skip(options.WIRE)('Frame SlowMo dispatchEvent', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.dispatchEvent('button', 'click')); -}); -it.skip(options.WIRE)('Frame SlowMo evaluate', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.evaluate(() => void 0)); -}); -it.skip(options.WIRE)('Frame SlowMo evaluateHandle', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.evaluateHandle(() => window)); -}); -it.skip(options.WIRE)('Frame SlowMo fill', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.fill('.fill', 'foo')); -}); -it.skip(options.WIRE)('Frame SlowMo focus', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.focus('button')); -}); -it.skip(options.WIRE)('Frame SlowMo goto', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.goto('about:blank')); -}); -it.skip(options.WIRE)('Frame SlowMo hover', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.hover('button')); -}); -it.skip(options.WIRE)('Frame SlowMo press', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.press('button', 'Enter')); -}); -it.skip(options.WIRE)('Frame SlowMo setContent', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.setContent('hello world')); -}); -it.skip(options.WIRE)('Frame SlowMo selectOption', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.selectOption('select', 'foo')); -}); -it.skip(options.WIRE)('Frame SlowMo setInputFiles', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.setInputFiles('.file', [])); -}); -it.skip(options.WIRE)('Frame SlowMo type', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.type('.fill', 'a')); -}); -it.skip(options.WIRE)('Frame SlowMo uncheck', async ({page, server, toImpl}) => { - await checkFrameSlowMo(toImpl, page, server, frame => frame.uncheck('.uncheck')); -}); async function checkElementSlowMo(toImpl, page, selector, task) { await page.setContent(` @@ -193,51 +242,3 @@ async function checkElementSlowMo(toImpl, page, selector, task) { const element = await page.$(selector); await checkSlowMo(toImpl, page, task.bind(null, element)); } -it.skip(options.WIRE)('ElementHandle SlowMo $$eval', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'body', element => element.$$eval('button', () => void 0)); -}); -it.skip(options.WIRE)('ElementHandle SlowMo $eval', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'body', element => element.$eval('button', () => void 0)); -}); -it.skip(options.WIRE)('ElementHandle SlowMo check', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, '.check', element => element.check()); -}); -it.skip(options.WIRE)('ElementHandle SlowMo click', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.click()); -}); -it.skip(options.WIRE)('ElementHandle SlowMo dblclick', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.dblclick()); -}); -it.skip(options.WIRE)('ElementHandle SlowMo dispatchEvent', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.dispatchEvent('click')); -}); -it.skip(options.WIRE)('ElementHandle SlowMo evaluate', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.evaluate(() => void 0)); -}); -it.skip(options.WIRE)('ElementHandle SlowMo evaluateHandle', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.evaluateHandle(() => void 0)); -}); -it.skip(options.WIRE)('ElementHandle SlowMo fill', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, '.fill', element => element.fill('foo')); -}); -it.skip(options.WIRE)('ElementHandle SlowMo focus', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.focus()); -}); -it.skip(options.WIRE)('ElementHandle SlowMo hover', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.hover()); -}); -it.skip(options.WIRE)('ElementHandle SlowMo press', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'button', element => element.press('Enter')); -}); -it.skip(options.WIRE)('ElementHandle SlowMo selectOption', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, 'select', element => element.selectOption('foo')); -}); -it.skip(options.WIRE)('ElementHandle SlowMo setInputFiles', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, '.file', element => element.setInputFiles([])); -}); -it.skip(options.WIRE)('ElementHandle SlowMo type', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, '.fill', element => element.type('a')); -}); -it.skip(options.WIRE)('ElementHandle SlowMo uncheck', async ({page, toImpl}) => { - await checkElementSlowMo(toImpl, page, '.uncheck', element => element.uncheck()); -}); diff --git a/test/snapshot.spec.ts b/test/snapshot.spec.ts index 2e23fcdf2c..678828f9e8 100644 --- a/test/snapshot.spec.ts +++ b/test/snapshot.spec.ts @@ -16,7 +16,9 @@ import { options } from './playwright.fixtures'; -it.skip(!options.TRACING)('should not throw', async ({page, server, context, toImpl}) => { +it('should not throw', test => { + test.skip(!options.TRACING); +}, async ({page, server, context, toImpl}) => { await page.goto(server.PREFIX + '/snapshot/snapshot-with-css.html'); await (context as any).__snapshotter.captureSnapshot(toImpl(page), { timeout: 5000, label: 'snapshot' }); });