feat(test): introduce metafunc for skip (#3676)

This commit is contained in:
Pavel Feldman 2020-08-28 13:53:47 -07:00 committed by GitHub
parent e5ff283a40
commit 97e4561ee1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 2547 additions and 2229 deletions

View file

@ -20,32 +20,29 @@ import { promisify } from 'util';
import fs from 'fs'; import fs from 'fs';
import rimraf from 'rimraf'; import rimraf from 'rimraf';
import { registerFixture } from './fixtures'; import { registerFixture } from './fixtures';
import { Test } from './test';
interface Describers<STATE> {
it(name: string, inner: (state: STATE) => Promise<void> | void): void;
it(name: string, modifier: (test: Test) => any, inner: (state: STATE) => Promise<void> | void): void;
}
declare global { declare global {
type DescribeFunction = ((name: string, inner: () => void) => void) & { type DescribeFunction = ((name: string, inner: () => void) => void) & {
fail(condition: boolean): DescribeFunction; fail(condition: boolean): DescribeFunction;
skip(condition: boolean): DescribeFunction; skip(condition: boolean): DescribeFunction;
fixme(condition: boolean): DescribeFunction;
flaky(condition: boolean): DescribeFunction;
slow(): DescribeFunction; slow(): DescribeFunction;
repeat(n: number): DescribeFunction; repeat(n: number): DescribeFunction;
}; };
type ItFunction<STATE> = ((name: string, inner: (state: STATE) => Promise<void> | void) => void) & {
fail(condition: boolean): ItFunction<STATE>;
fixme(condition: boolean): ItFunction<STATE>;
flaky(condition: boolean): ItFunction<STATE>;
skip(condition: boolean): ItFunction<STATE>;
slow(): ItFunction<STATE>;
repeat(n: number): ItFunction<STATE>;
};
const describe: DescribeFunction; const describe: DescribeFunction;
const fdescribe: DescribeFunction; const fdescribe: DescribeFunction;
const xdescribe: DescribeFunction; const xdescribe: DescribeFunction;
const it: ItFunction<TestState & WorkerState & FixtureParameters>; const it: Describers<TestState & WorkerState & FixtureParameters>['it'];
const fit: ItFunction<TestState & WorkerState & FixtureParameters>; const fit: Describers<TestState & WorkerState & FixtureParameters>['it'];
const dit: ItFunction<TestState & WorkerState & FixtureParameters>; const xit: Describers<TestState & WorkerState & FixtureParameters>['it'];
const xit: ItFunction<TestState & WorkerState & FixtureParameters>;
const beforeEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void; const beforeEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void;
const afterEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void; const afterEach: (inner: (state: TestState & WorkerState & FixtureParameters) => Promise<void>) => void;

View file

@ -176,6 +176,7 @@ export class Runner {
const { test } = this._testById.get(params.id); const { test } = this._testById.get(params.id);
test._skipped = params.skipped; test._skipped = params.skipped;
test._flaky = params.flaky; test._flaky = params.flaky;
test._slow = params.slow;
test._expectedStatus = params.expectedStatus; test._expectedStatus = params.expectedStatus;
this._reporter.onTestBegin(test); this._reporter.onTestBegin(test);
}); });

View file

@ -53,32 +53,31 @@ export function spec(suite: Suite, file: string, timeout: number): () => void {
const suites = [suite]; const suites = [suite];
suite.file = file; 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]; const suite = suites[0];
if (typeof fn !== 'function') {
fn = metaFn;
metaFn = null;
}
const test = new Test(title, fn); const test = new Test(title, fn);
if (metaFn)
metaFn(test);
test.file = file; test.file = file;
test.slow = specs.slow && specs.slow[0];
test.timeout = timeout; test.timeout = timeout;
const only = specs._only && specs._only[0];
const only = specs.only && specs.only[0];
if (only) if (only)
test.only = true; test.only = true;
if (!only && specs.skip && specs.skip[0]) if (!only && specs._skip && specs._skip[0])
test._skipped = true; 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); suite._addTest(test);
return 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]); const child = new Suite(title, suites[0]);
suites[0]._addSuite(child); suites[0]._addSuite(child);
child.file = file; child.file = file;
child._slow = specs.slow && specs.slow[0];
const only = specs.only && specs.only[0]; const only = specs.only && specs.only[0];
if (only) if (only)
child.only = true; child.only = true;
@ -86,6 +85,8 @@ export function spec(suite: Suite, file: string, timeout: number): () => void {
child._skipped = true; child._skipped = true;
if (!only && specs.fixme && specs.fixme[0]) if (!only && specs.fixme && specs.fixme[0])
child._skipped = true; child._skipped = true;
if (specs.flaky && specs.flaky[0])
child._flaky = true;
suites.unshift(child); suites.unshift(child);
fn(); fn();
suites.shift(); 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).fdescribe = describe.only(true);
(global as any).xdescribe = describe.skip(true); (global as any).xdescribe = describe.skip(true);
(global as any).it = it; (global as any).it = it;
(global as any).fit = it.only(true); (global as any).fit = it._only(true);
(global as any).xit = it.skip(true); (global as any).xit = it._skip(true);
return installTransform(); return installTransform();
} }

View file

@ -23,7 +23,6 @@ export class Test {
title: string; title: string;
file: string; file: string;
only = false; only = false;
slow = false;
timeout = 0; timeout = 0;
fn: Function; fn: Function;
results: TestResult[] = []; results: TestResult[] = [];
@ -33,9 +32,11 @@ export class Test {
// We will compute them there and send to the runner (front-end) // We will compute them there and send to the runner (front-end)
_skipped = false; _skipped = false;
_flaky = false; _flaky = false;
_slow = false;
_expectedStatus: TestStatus = 'passed';
_overriddenFn: Function; _overriddenFn: Function;
_startTime: number; _startTime: number;
_expectedStatus: TestStatus = 'passed';
constructor(title: string, fn: Function) { constructor(title: string, fn: Function) {
this.title = title; this.title = title;
@ -50,6 +51,56 @@ export class Test {
return this.titlePath().join(' '); 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 { _appendResult(): TestResult {
const result: TestResult = { const result: TestResult = {
duration: 0, duration: 0,
@ -85,6 +136,7 @@ export class Test {
test.file = this.file; test.file = this.file;
test.timeout = this.timeout; test.timeout = this.timeout;
test._flaky = this._flaky; test._flaky = this._flaky;
test._slow = this._slow;
test._overriddenFn = this._overriddenFn; test._overriddenFn = this._overriddenFn;
return test; return test;
} }
@ -107,6 +159,8 @@ export class Suite {
tests: Test[] = []; tests: Test[] = [];
only = false; only = false;
file: string; file: string;
_flaky = false;
_slow = false;
configuration: Configuration; configuration: Configuration;
// Skipped & flaky are resolved based on options in worker only // 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()); 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) { _addTest(test: Test) {
test.suite = this; test.suite = this;
this.tests.push(test); this.tests.push(test);
@ -176,7 +238,9 @@ export class Suite {
const suite = new Suite(this.title); const suite = new Suite(this.title);
suite.only = this.only; suite.only = this.only;
suite.file = this.file; suite.file = this.file;
suite._flaky = this._flaky;
suite._skipped = this._skipped; suite._skipped = this._skipped;
suite._slow = this._slow;
return suite; return suite;
} }

View file

@ -155,10 +155,13 @@ export class TestRunner extends EventEmitter {
// We only know resolved skipped/flaky value in the worker, // We only know resolved skipped/flaky value in the worker,
// send it to the runner. // send it to the runner.
test._skipped = test._skipped || test.suite._isSkipped(); test._skipped = test._skipped || test.suite._isSkipped();
test._flaky = test._flaky || test.suite._isFlaky();
test._slow = test._slow || test.suite._isSlow();
this.emit('testBegin', { this.emit('testBegin', {
id, id,
skipped: test._skipped, skipped: test._skipped,
flaky: test._flaky, flaky: test._flaky,
slow: test._slow
}); });
const result: TestResult = { const result: TestResult = {
@ -182,7 +185,7 @@ export class TestRunner extends EventEmitter {
const testInfo = { config: this._config, test, result }; const testInfo = { config: this._config, test, result };
if (!this._trialRun) { if (!this._trialRun) {
await this._runHooks(test.suite, 'beforeEach', 'before', testInfo); 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 fixturePool.runTestWithFixtures(test.fn, timeout, testInfo);
await this._runHooks(test.suite, 'afterEach', 'after', testInfo); await this._runHooks(test.suite, 'afterEach', 'after', testInfo);
} else { } else {

View file

@ -17,7 +17,9 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
it.flaky('flake', async ({}) => { it('flake', test => {
test.flaky();
}, async ({}) => {
try { try {
fs.readFileSync(path.join(__dirname, '..', 'test-results', 'allow-flaky.txt')); fs.readFileSync(path.join(__dirname, '..', 'test-results', 'allow-flaky.txt'));
} catch (e) { } catch (e) {

View file

@ -15,7 +15,7 @@
*/ */
require('../../lib'); require('../../lib');
it.fail(true)('fails',() => { it('fails', test => test.fail(), () => {
expect(1 + 1).toBe(3); expect(1 + 1).toBe(3);
}); });

View file

@ -15,6 +15,6 @@
*/ */
require('../../'); require('../../');
it('fails',() => { it('fails', () => {
expect(1 + 1).toBe(7); expect(1 + 1).toBe(7);
}); });

View file

@ -15,6 +15,6 @@
*/ */
require('../../'); require('../../');
it('succeeds',() => { it('succeeds', () => {
expect(1 + 1).toBe(2); expect(1 + 1).toBe(2);
}); });

View file

@ -15,6 +15,6 @@
*/ */
require('../../'); require('../../');
it.fail(true)('succeeds',() => { it('succeeds', test => test.fail(), () => {
expect(1 + 1).toBe(2); expect(1 + 1).toBe(2);
}); });

View file

@ -138,8 +138,9 @@ it('should not report text nodes inside controls', async function({page}) {
expect(await page.accessibility.snapshot()).toEqual(golden); expect(await page.accessibility.snapshot()).toEqual(golden);
}); });
// WebKit rich text accessibility is iffy it('rich text editable fields should have children', test => {
it.skip(options.WEBKIT)('rich text editable fields should have children', async function({page}) { test.skip(options.WEBKIT, 'WebKit rich text accessibility is iffy');
}, async function({page}) {
await page.setContent(` await page.setContent(`
<div contenteditable="true"> <div contenteditable="true">
Edit this image: <img src="fakeimage.png" alt="my fake image"> Edit this image: <img src="fakeimage.png" alt="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(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual(golden); 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(` await page.setContent(`
<div contenteditable="true" role='textbox'> <div contenteditable="true" role='textbox'>
Edit this image: <img src="fakeimage.png" alt="my fake image"> Edit this image: <img src="fakeimage.png" alt="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); 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". // Firefox does not support contenteditable="plaintext-only".
// WebKit rich text accessibility is iffy // WebKit rich text accessibility is iffy
await page.setContent(` it('plain text field with role should not have children', async function({page}) {
<div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`); await page.setContent(`
const snapshot = await page.accessibility.snapshot(); <div contenteditable="plaintext-only" role='textbox'>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
expect(snapshot.children[0]).toEqual({ const snapshot = await page.accessibility.snapshot();
role: 'textbox', expect(snapshot.children[0]).toEqual({
name: '', role: 'textbox',
value: 'Edit this image:' name: '',
value: 'Edit this image:'
});
}); });
});
it.skip(options.FIREFOX || options.WEBKIT)('plain text field without role should not have content', async function({page}) { it('plain text field without role should not have content', async function({page}) {
await page.setContent(` await page.setContent(`
<div contenteditable="plaintext-only">Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`); <div contenteditable="plaintext-only">Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
const snapshot = await page.accessibility.snapshot(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual({ expect(snapshot.children[0]).toEqual({
role: 'generic', role: 'generic',
name: '' name: ''
});
}); });
});
it.skip(options.FIREFOX || options.WEBKIT)('plain text field with tabindex and without role should not have content', async function({page}) { it('plain text field with tabindex and without role should not have content', async function({page}) {
await page.setContent(` await page.setContent(`
<div contenteditable="plaintext-only" tabIndex=0>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`); <div contenteditable="plaintext-only" tabIndex=0>Edit this image:<img src="fakeimage.png" alt="my fake image"></div>`);
const snapshot = await page.accessibility.snapshot(); const snapshot = await page.accessibility.snapshot();
expect(snapshot.children[0]).toEqual({ expect(snapshot.children[0]).toEqual({
role: 'generic', role: 'generic',
name: '' name: ''
});
}); });
}); });

View file

@ -200,7 +200,9 @@ it('should work with goto following click', async ({page, server}) => {
await page.goto(server.EMPTY_PAGE); 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(`<a href="${server.PREFIX + '/frames/one-frame.html'}">click me</a>`); await page.setContent(`<a href="${server.PREFIX + '/frames/one-frame.html'}">click me</a>`);
const __testHookAfterPointerAction = () => new Promise(f => setTimeout(f, 6000)); const __testHookAfterPointerAction = () => new Promise(f => setTimeout(f, 6000));
const error = await page.click('a', { timeout: 5000, __testHookAfterPointerAction } as any).catch(e => e); const error = await page.click('a', { timeout: 5000, __testHookAfterPointerAction } as any).catch(e => e);

View file

@ -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 browser1 = await browserType.launch(defaultBrowserOptions);
const context1 = await browser1.newContext(); const context1 = await browser1.newContext();
await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]); await context1.addCookies([{url: server.EMPTY_PAGE, name: 'cookie-in-context-1', value: 'value', expires: Date.now() / 1000 + 10000}]);

View file

@ -72,7 +72,9 @@ it('should properly report httpOnly cookie', async ({context, page, server}) =>
expect(cookies[0].httpOnly).toBe(true); 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) => { server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', 'name=value;SameSite=Strict'); res.setHeader('Set-Cookie', 'name=value;SameSite=Strict');
res.end(); res.end();
@ -83,7 +85,9 @@ it.fail(options.WEBKIT && WIN)('should properly report "Strict" sameSite cookie'
expect(cookies[0].sameSite).toBe('Strict'); 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) => { server.setRoute('/empty.html', (req, res) => {
res.setHeader('Set-Cookie', 'name=value;SameSite=Lax'); res.setHeader('Set-Cookie', 'name=value;SameSite=Lax');
res.end(); res.end();

View file

@ -16,7 +16,9 @@
*/ */
import { options } from './playwright.fixtures'; 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'); server.setAuth('/empty.html', 'user', 'pass');
const context = await browser.newContext(); const context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
@ -25,7 +27,9 @@ it.fail(options.CHROMIUM && !options.HEADLESS)('should fail without credentials'
await context.close(); 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'); server.setAuth('/empty.html', 'user', 'pass');
const context = await browser.newContext(); const context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();

View file

@ -16,41 +16,43 @@
*/ */
import { options } from './playwright.fixtures'; import { options } from './playwright.fixtures';
it.skip(options.FIREFOX)('should work', async ({playwright, browser, server}) => { describe.skip(options.FIREFOX)('device', () => {
const iPhone = playwright.devices['iPhone 6']; it('should work', async ({playwright, browser, server}) => {
const context = await browser.newContext({ ...iPhone }); const iPhone = playwright.devices['iPhone 6'];
const page = await context.newPage(); const context = await browser.newContext({ ...iPhone });
await page.goto(server.PREFIX + '/mobile.html'); const page = await context.newPage();
expect(await page.evaluate(() => window.innerWidth)).toBe(375); await page.goto(server.PREFIX + '/mobile.html');
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone'); expect(await page.evaluate(() => window.innerWidth)).toBe(375);
await context.close(); 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']; it('should support clicking', async ({playwright, browser, server}) => {
const context = await browser.newContext({ ...iPhone }); const iPhone = playwright.devices['iPhone 6'];
const page = await context.newPage(); const context = await browser.newContext({ ...iPhone });
await page.goto(server.PREFIX + '/input/button.html'); const page = await context.newPage();
const button = await page.$('button'); await page.goto(server.PREFIX + '/input/button.html');
await page.evaluate(button => button.style.marginTop = '200px', button); const button = await page.$('button');
await button.click(); await page.evaluate(button => button.style.marginTop = '200px', button);
expect(await page.evaluate('result')).toBe('Clicked'); await button.click();
await context.close(); 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({ it('should scroll to click', async ({browser, server}) => {
viewport: { const context = await browser.newContext({
width: 400, viewport: {
height: 400, width: 400,
}, height: 400,
deviceScaleFactor: 1, },
isMobile: true 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();
}); });

View file

@ -156,8 +156,9 @@ it('should fire page lifecycle events', async function({browser, server}) {
await context.close(); await context.close();
}); });
it.fixme(options.WEBKIT)('should work with Shift-clicking', async ({browser, server}) => { it('should work with Shift-clicking', test => {
// WebKit: Shift+Click does not open a new window. test.fixme(options.WEBKIT, 'WebKit: Shift+Click does not open a new window.');
}, async ({browser, server}) => {
const context = await browser.newContext(); const context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); 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(); await context.close();
}); });
it.fixme(options.WEBKIT || options.FIREFOX)('should work with Ctrl-clicking', async ({browser, server}) => { it('should work with Ctrl-clicking', test => {
// Firefox: reports an opener in this case. test.fixme(options.WEBKIT, 'Ctrl+Click does not open a new tab.');
// 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 context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);

View file

@ -17,113 +17,115 @@
import { options } from './playwright.fixtures'; import { options } from './playwright.fixtures';
it.skip(options.FIREFOX)('should support mobile emulation', async ({playwright, browser, server}) => { describe.skip(options.FIREFOX)('mobile viewport', () => {
const iPhone = playwright.devices['iPhone 6']; it('should support mobile emulation', async ({playwright, browser, server}) => {
const context = await browser.newContext({ ...iPhone }); const iPhone = playwright.devices['iPhone 6'];
const page = await context.newPage(); const context = await browser.newContext({ ...iPhone });
await page.goto(server.PREFIX + '/mobile.html'); const page = await context.newPage();
expect(await page.evaluate(() => window.innerWidth)).toBe(375); await page.goto(server.PREFIX + '/mobile.html');
await page.setViewportSize({width: 400, height: 300}); expect(await page.evaluate(() => window.innerWidth)).toBe(375);
expect(await page.evaluate(() => window.innerWidth)).toBe(400); await page.setViewportSize({width: 400, height: 300});
await context.close(); 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));
}); });
const event1 = page.waitForEvent('console'); it('should support touch emulation', async ({playwright, browser, server}) => {
await page.setViewportSize({width: 400, height: 300}); const iPhone = playwright.devices['iPhone 6'];
expect((await event1).text()).toBe('1'); 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'); function dispatchTouch() {
await page.setViewportSize({width: 300, height: 400}); let fulfill;
expect((await event2).text()).toBe('2'); const promise = new Promise(x => fulfill = x);
await context.close(); 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}) => { fulfill('Did not receive touch');
const context = await browser.newContext({ viewport: {width: 320, height: 480 }, isMobile: true });
const page = await context.newPage();
await page.goto(server.PREFIX + '/empty.html');
expect(await page.evaluate(() => window.innerWidth)).toBe(980);
await context.close();
});
it.skip(options.FIREFOX)('respect meta viewport tag', async ({browser, server}) => { return promise;
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); it('should be detectable by Modernizr', async ({playwright, browser, server}) => {
await context.close(); 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();
});
}); });

View file

@ -18,7 +18,9 @@
import fs from 'fs'; import fs from 'fs';
import { options } from './playwright.fixtures'; 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(); const executablePath = browserType.executablePath();
expect(fs.existsSync(executablePath)).toBe(true); expect(fs.existsSync(executablePath)).toBe(true);
expect(fs.realpathSync(executablePath)).toBe(executablePath); expect(fs.realpathSync(executablePath)).toBe(executablePath);

View file

@ -19,162 +19,166 @@ import { options } from './playwright.fixtures';
import utils from './utils'; import utils from './utils';
import './remoteServer.fixture'; import './remoteServer.fixture';
it.skip(options.WIRE).slow()('should be able to reconnect to a browser', async ({browserType, remoteServer, server}) => { 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(); const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
expect(browserContext.pages().length).toBe(0); const browserContext = await browser.newContext();
const page = await browserContext.newPage(); expect(browserContext.pages().length).toBe(0);
expect(await page.evaluate('11 * 11')).toBe(121); const page = await browserContext.newPage();
await page.goto(server.EMPTY_PAGE); expect(await page.evaluate('11 * 11')).toBe(121);
await browser.close(); await page.goto(server.EMPTY_PAGE);
} await browser.close();
{ }
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); {
const browserContext = await browser.newContext(); const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
const page = await browserContext.newPage(); const browserContext = await browser.newContext();
await page.goto(server.EMPTY_PAGE); const page = await browserContext.newPage();
await browser.close(); 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));
} }
}); });
await utils.registerEngine(playwright, 'mycss', mycss);
const browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() }); it('should be able to connect two browsers at the same time', async ({browserType, remoteServer}) => {
const page = await browser.newPage(); const browser1 = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
await page.setContent(`<div>hello</div>`); expect(browser1.contexts().length).toBe(0);
expect(await page.innerHTML('css=div')).toBe('hello'); await browser1.newContext();
expect(await page.innerHTML('mycss=div')).toBe('hello'); expect(browser1.contexts().length).toBe(1);
await browser.close();
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(`<div>hello</div>`);
expect(await page.innerHTML('css=div')).toBe('hello');
expect(await page.innerHTML('mycss=div')).toBe('hello');
await browser.close();
});
}); });

View file

@ -17,38 +17,40 @@
import { options } from './playwright.fixtures'; import { options } from './playwright.fixtures';
it.skip(options.WIRE)('should work', async ({browserType, defaultBrowserOptions}) => { describe.skip(options.WIRE)('lauch server', () => {
const browserServer = await browserType.launchServer(defaultBrowserOptions); it('should work', async ({browserType, defaultBrowserOptions}) => {
expect(browserServer.wsEndpoint()).not.toBe(null); const browserServer = await browserType.launchServer(defaultBrowserOptions);
await browserServer.close(); expect(browserServer.wsEndpoint()).not.toBe(null);
}); await browserServer.close();
});
it.skip(options.WIRE)('should fire "close" event during kill', async ({browserType, defaultBrowserOptions}) => { it('should fire "close" event during kill', async ({browserType, defaultBrowserOptions}) => {
const order = []; const order = [];
const browserServer = await browserType.launchServer(defaultBrowserOptions); const browserServer = await browserType.launchServer(defaultBrowserOptions);
const closedPromise = new Promise(f => browserServer.on('close', () => { const closedPromise = new Promise(f => browserServer.on('close', () => {
order.push('closed'); order.push('closed');
f(); f();
})); }));
await Promise.all([ await Promise.all([
browserServer.kill().then(() => order.push('killed')), browserServer.kill().then(() => order.push('killed')),
closedPromise, closedPromise,
]); ]);
expect(order).toEqual(['closed', 'killed']); expect(order).toEqual(['closed', 'killed']);
}); });
it.skip(options.WIRE)('should return child_process instance', async ({browserType, defaultBrowserOptions}) => { it('should return child_process instance', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions); const browserServer = await browserType.launchServer(defaultBrowserOptions);
expect(browserServer.process().pid).toBeGreaterThan(0); expect(browserServer.process().pid).toBeGreaterThan(0);
await browserServer.close(); await browserServer.close();
}); });
it.skip(options.WIRE)('should fire close event', async ({browserType, defaultBrowserOptions}) => { it('should fire close event', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions); const browserServer = await browserType.launchServer(defaultBrowserOptions);
const [result] = await Promise.all([ const [result] = await Promise.all([
new Promise(f => (browserServer as any).on('close', (exitCode, signal) => f({ exitCode, signal }))), new Promise(f => (browserServer as any).on('close', (exitCode, signal) => f({ exitCode, signal }))),
browserServer.close(), browserServer.close(),
]); ]);
expect(result['exitCode']).toBe(0); expect(result['exitCode']).toBe(0);
expect(result['signal']).toBe(null); expect(result['signal']).toBe(null);
});
}); });

View file

@ -36,15 +36,18 @@ it('should throw if userDataDir option is passed', async ({browserType, defaultB
expect(waitError.message).toContain('launchPersistentContext'); 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; let waitError = null;
const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] }); const options = Object.assign({}, defaultBrowserOptions, { args: ['http://example.com'] });
await browserType.launch(options).catch(e => waitError = e); await browserType.launch(options).catch(e => waitError = e);
expect(waitError.message).toContain('can not specify page'); expect(waitError.message).toContain('can not specify page');
}); });
it.fixme(true)('should reject if launched browser fails immediately', async ({browserType, defaultBrowserOptions}) => { it('should reject if launched browser fails immediately', test => {
// I'm getting ENCONRESET on this one. 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')}); const options = Object.assign({}, defaultBrowserOptions, {executablePath: path.join(__dirname, 'assets', 'dummy_bad_browser_executable.js')});
let waitError = null; let waitError = null;
await browserType.launch(options).catch(e => waitError = e); 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'); 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 options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
const error = await browserType.launch(options).catch(e => e); const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain(`browserType.launch: Timeout 5000ms exceeded.`); 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(`<launched> pid=`); expect(error.message).toContain(`<launched> 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 e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 }; const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 };
const error = await browserType.launch(options).catch(e => e); const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain('Dummy'); 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 e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 }; const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; }, timeout: 9000 };
const error = await browserType.launch(options).catch(e => e); const error = await browserType.launch(options).catch(e => e);
expect(error.message).toContain('<launching>'); expect(error.message).toContain('<launching>');
}); });
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); const browser = await browserType.launch({ ...defaultBrowserOptions, process } as any);
await browser.close(); await browser.close();
}); });

View file

@ -17,7 +17,9 @@
import url from 'url'; import url from 'url';
import { options } from './playwright.fixtures'; 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'); await page.goto(server.PREFIX + '/wasm/table2.html');
expect(await page.evaluate('loadTable()')).toBe('42, 83'); 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'); 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, // TODO: the test passes on Windows locally but fails on GitHub Action bot,
// apparently due to a Media Pack issue in the Windows Server. // apparently due to a Media Pack issue in the Windows Server.
// Also the test is very flaky on Linux WebKit. // Also the test is very flaky on Linux WebKit.

View file

@ -65,7 +65,9 @@ it('should scope context handles', async ({browserType, browser, server}) => {
await expectScopeState(browser, GOLDEN_PRECONDITION); 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 = { const GOLDEN_PRECONDITION = {
_guid: '', _guid: '',
objects: [ objects: [

View file

@ -15,119 +15,121 @@
*/ */
import { options } from './playwright.fixtures'; import { options } from './playwright.fixtures';
it.skip(!options.CHROMIUM)('should work', async function({browserType, page, server}) { describe.skip(!options.CHROMIUM)('oopif', () => {
await page.coverage.startCSSCoverage(); it('should work', async function({browserType, page, server}) {
await page.goto(server.PREFIX + '/csscoverage/simple.html'); await page.coverage.startCSSCoverage();
const coverage = await page.coverage.stopCSSCoverage(); await page.goto(server.PREFIX + '/csscoverage/simple.html');
expect(coverage.length).toBe(1); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage[0].url).toContain('/csscoverage/simple.html'); expect(coverage.length).toBe(1);
expect(coverage[0].ranges).toEqual([ expect(coverage[0].url).toContain('/csscoverage/simple.html');
{start: 1, end: 22} 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; }'); 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}) { it('should report sourceURLs', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/sourceurl.html'); await page.goto(server.PREFIX + '/csscoverage/sourceurl.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
expect(coverage[0].url).toBe('nicename.css'); expect(coverage[0].url).toBe('nicename.css');
}); });
it.skip(!options.CHROMIUM)('should report multiple stylesheets', async function({page, server}) { it('should report multiple stylesheets', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.PREFIX + '/csscoverage/multiple.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(2); expect(coverage.length).toBe(2);
coverage.sort((a, b) => a.url.localeCompare(b.url)); coverage.sort((a, b) => a.url.localeCompare(b.url));
expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css'); expect(coverage[0].url).toContain('/csscoverage/stylesheet1.css');
expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css'); expect(coverage[1].url).toContain('/csscoverage/stylesheet2.css');
}); });
it.skip(!options.CHROMIUM)('should report stylesheets that have no coverage', async function({page, server}) { it('should report stylesheets that have no coverage', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/unused.html'); await page.goto(server.PREFIX + '/csscoverage/unused.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
expect(coverage[0].url).toBe('unused.css'); expect(coverage[0].url).toBe('unused.css');
expect(coverage[0].ranges.length).toBe(0); expect(coverage[0].ranges.length).toBe(0);
}); });
it.skip(!options.CHROMIUM)('should work with media queries', async function({page, server}) { it('should work with media queries', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/media.html'); await page.goto(server.PREFIX + '/csscoverage/media.html');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
expect(coverage[0].url).toContain('/csscoverage/media.html'); expect(coverage[0].url).toContain('/csscoverage/media.html');
expect(coverage[0].ranges).toEqual([ expect(coverage[0].ranges).toEqual([
{start: 17, end: 38} {start: 17, end: 38}
]); ]);
}); });
it.skip(!options.CHROMIUM)('should work with complicated usecases', async function({page, server}) { it('should work with complicated usecases', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.goto(server.PREFIX + '/csscoverage/involved.html'); await page.goto(server.PREFIX + '/csscoverage/involved.html');
const coverage: any = await page.coverage.stopCSSCoverage(); const coverage: any = await page.coverage.stopCSSCoverage();
delete coverage[0].text; delete coverage[0].text;
delete coverage[0].url; delete coverage[0].url;
expect(coverage).toEqual( expect(coverage).toEqual(
[ [
{ {
'ranges': [ 'ranges': [
{ {
'start': 149, 'start': 149,
'end': 297 'end': 297
}, },
{ {
'start': 327, 'start': 327,
'end': 433 'end': 433
} }
] ]
} }
] ]
); );
}); });
it.skip(!options.CHROMIUM)('should ignore injected stylesheets', async function({page, server}) { it('should ignore injected stylesheets', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.addStyleTag({content: 'body { margin: 10px;}'}); await page.addStyleTag({content: 'body { margin: 10px;}'});
// trigger style recalc // trigger style recalc
const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin); const margin = await page.evaluate(() => window.getComputedStyle(document.body).margin);
expect(margin).toBe('10px'); expect(margin).toBe('10px');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(0); expect(coverage.length).toBe(0);
}); });
it.skip(!options.CHROMIUM)('should report stylesheets across navigations', async function({page, server}) { it('should report stylesheets across navigations', async function({page, server}) {
await page.coverage.startCSSCoverage({resetOnNavigation: false}); await page.coverage.startCSSCoverage({resetOnNavigation: false});
await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.PREFIX + '/csscoverage/multiple.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(2); expect(coverage.length).toBe(2);
}); });
it.skip(!options.CHROMIUM)('should NOT report scripts across navigations', async function({page, server}) { it('should NOT report scripts across navigations', async function({page, server}) {
await page.coverage.startCSSCoverage(); // Enabled by default. await page.coverage.startCSSCoverage(); // Enabled by default.
await page.goto(server.PREFIX + '/csscoverage/multiple.html'); await page.goto(server.PREFIX + '/csscoverage/multiple.html');
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(0); expect(coverage.length).toBe(0);
}); });
it.skip(!options.CHROMIUM)('should work with a recently loaded stylesheet', async function({page, server}) { it('should work with a recently loaded stylesheet', async function({page, server}) {
await page.coverage.startCSSCoverage(); await page.coverage.startCSSCoverage();
await page.evaluate(async url => { await page.evaluate(async url => {
document.body.textContent = 'hello, world'; document.body.textContent = 'hello, world';
const link = document.createElement('link'); const link = document.createElement('link');
link.rel = 'stylesheet'; link.rel = 'stylesheet';
link.href = url; link.href = url;
document.head.appendChild(link); document.head.appendChild(link);
await new Promise(x => link.onload = x); await new Promise(x => link.onload = x);
await new Promise(f => requestAnimationFrame(f)); await new Promise(f => requestAnimationFrame(f));
}, server.PREFIX + '/csscoverage/stylesheet1.css'); }, server.PREFIX + '/csscoverage/stylesheet1.css');
const coverage = await page.coverage.stopCSSCoverage(); const coverage = await page.coverage.stopCSSCoverage();
expect(coverage.length).toBe(1); expect(coverage.length).toBe(1);
});
}); });

View file

@ -15,82 +15,87 @@
*/ */
import { options } from './playwright.fixtures'; 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); expect(page.coverage).toBe(null);
}); });
it.skip(!options.CHROMIUM)('should work', async function({browserType, page, server}) { describe.skip(!options.CHROMIUM)('oopif', () => {
await page.coverage.startJSCoverage(); it('should work', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); await page.coverage.startJSCoverage();
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' });
expect(coverage.length).toBe(1); const coverage = await page.coverage.stopJSCoverage();
expect(coverage[0].url).toContain('/jscoverage/simple.html'); expect(coverage.length).toBe(1);
expect(coverage[0].functions.find(f => f.functionName === 'foo').ranges[0].count).toEqual(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(); it('should report sourceURLs', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/sourceurl.html'); await page.coverage.startJSCoverage();
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/sourceurl.html');
expect(coverage.length).toBe(1); const coverage = await page.coverage.stopJSCoverage();
expect(coverage[0].url).toBe('nicename.js'); 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(); it('should ignore eval() scripts by default', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/eval.html'); await page.coverage.startJSCoverage();
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/eval.html');
expect(coverage.length).toBe(1); 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}); it('shouldn\'t ignore eval() scripts if reportAnonymousScripts is true', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/eval.html'); await page.coverage.startJSCoverage({reportAnonymousScripts: true});
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/eval.html');
expect(coverage.find(entry => entry.url === '').source).toBe('console.log("foo")'); const coverage = await page.coverage.stopJSCoverage();
expect(coverage.length).toBe(2); 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}); it('should ignore playwright internal scripts if reportAnonymousScripts is true', async function({page, server}) {
await page.goto(server.EMPTY_PAGE); await page.coverage.startJSCoverage({reportAnonymousScripts: true});
await page.evaluate('console.log("foo")'); await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => console.log('bar')); await page.evaluate('console.log("foo")');
const coverage = await page.coverage.stopJSCoverage(); await page.evaluate(() => console.log('bar'));
expect(coverage.length).toBe(0); 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(); it('should report multiple scripts', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.coverage.startJSCoverage();
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.PREFIX + '/jscoverage/multiple.html');
expect(coverage.length).toBe(2); const coverage = await page.coverage.stopJSCoverage();
coverage.sort((a, b) => a.url.localeCompare(b.url)); expect(coverage.length).toBe(2);
expect(coverage[0].url).toContain('/jscoverage/script1.js'); coverage.sort((a, b) => a.url.localeCompare(b.url));
expect(coverage[1].url).toContain('/jscoverage/script2.js'); 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}); it('should report scripts across navigations when disabled', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.coverage.startJSCoverage({resetOnNavigation: false});
await page.goto(server.EMPTY_PAGE); await page.goto(server.PREFIX + '/jscoverage/multiple.html');
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.EMPTY_PAGE);
expect(coverage.length).toBe(2); 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. it('should NOT report scripts across navigations when enabled', async function({page, server}) {
await page.goto(server.PREFIX + '/jscoverage/multiple.html'); await page.coverage.startJSCoverage(); // Enabled by default.
await page.goto(server.EMPTY_PAGE); await page.goto(server.PREFIX + '/jscoverage/multiple.html');
const coverage = await page.coverage.stopJSCoverage(); await page.goto(server.EMPTY_PAGE);
expect(coverage.length).toBe(0); 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(); it('should not hang when there is a debugger statement', async function({page, server}) {
await page.goto(server.EMPTY_PAGE); await page.coverage.startJSCoverage();
await page.evaluate(() => { await page.goto(server.EMPTY_PAGE);
debugger; // eslint-disable-line no-debugger await page.evaluate(() => {
debugger; // eslint-disable-line no-debugger
});
await page.coverage.stopJSCoverage();
}); });
await page.coverage.stopJSCoverage();
}); });

View file

@ -16,72 +16,74 @@
import { options } from '../playwright.fixtures'; import { options } from '../playwright.fixtures';
import type { ChromiumBrowserContext } from '../..'; import type { ChromiumBrowserContext } from '../..';
it.skip(!options.CHROMIUM)('should create a worker from a service worker', async ({page, server, context}) => { describe.skip(!options.CHROMIUM)('chromium', () => {
const [worker] = await Promise.all([ it('should create a worker from a service worker', async ({page, server, context}) => {
(context as ChromiumBrowserContext).waitForEvent('serviceworker'), const [worker] = await Promise.all([
page.goto(server.PREFIX + '/serviceworkers/empty/sw.html') (context as ChromiumBrowserContext).waitForEvent('serviceworker'),
]); page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
expect(await worker.evaluate(() => self.toString())).toBe('[object ServiceWorkerGlobalScope]'); ]);
}); 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(`
<script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
</script>
`));
server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => {
serverRequest = req;
res.end('console.log(1);');
}); });
await page.route('*', route => route.continue()); it('serviceWorkers() should return current workers', async ({page, server, context}) => {
await page.goto(server.PREFIX + '/intervention'); const [worker1] = await Promise.all([
// Check for feature URL substring rather than https://www.chromestatus.com to (context as ChromiumBrowserContext).waitForEvent('serviceworker'),
// make it work with Edgium. page.goto(server.PREFIX + '/serviceworkers/empty/sw.html')
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104'); ]);
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(`
<script>
document.write('<script src="${server.CROSS_PROCESS_PREFIX}/intervention.js">' + '</scr' + 'ipt>');
</script>
`));
server.setRedirect('/intervention.js', '/redirect.js');
let serverRequest = null;
server.setRoute('/redirect.js', (req, res) => {
serverRequest = req;
res.end('console.log(1);');
});
await page.route('*', route => route.continue());
await page.goto(server.PREFIX + '/intervention');
// Check for feature URL substring rather than https://www.chromestatus.com to
// make it work with Edgium.
expect(serverRequest.headers.intervention).toContain('feature/5718547946799104');
});
}); });

View file

@ -20,21 +20,27 @@ import utils from '../utils';
import type { ChromiumBrowser, ChromiumBrowserContext } from '../..'; import type { ChromiumBrowser, ChromiumBrowserContext } from '../..';
const { makeUserDataDir, removeUserDataDir } = utils; 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); const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-pipe'].concat(options.args || []); options.args = ['--remote-debugging-pipe'].concat(options.args || []);
const error = await browserType.launchServer(options).catch(e => e); const error = await browserType.launchServer(options).catch(e => e);
expect(error.message).toContain('Playwright manages remote debugging connection itself'); 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); const options = Object.assign({}, defaultBrowserOptions);
options.args = ['--remote-debugging-port=0'].concat(options.args || []); options.args = ['--remote-debugging-port=0'].concat(options.args || []);
const browser = await browserType.launchServer(options); const browser = await browserType.launchServer(options);
await browser.close(); 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; let devtoolsCallback;
const devtoolsPromise = new Promise(f => devtoolsCallback = f); const devtoolsPromise = new Promise(f => devtoolsCallback = f);
const __testHookForDevTools = devtools => devtools.__testHookOnBinding = parsed => { 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(); 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 userDataDir = await makeUserDataDir();
const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension'); const extensionPath = path.join(__dirname, '..', 'assets', 'simple-extension');
const extensionOptions = {...defaultBrowserOptions, const extensionOptions = {...defaultBrowserOptions,
@ -72,7 +80,9 @@ it.skip(!options.CHROMIUM)('should return background pages', async ({browserType
await removeUserDataDir(userDataDir); 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 browser = await browserType.launch(defaultBrowserOptions);
const browserSession = await (browser as ChromiumBrowser).newBrowserCDPSession(); const browserSession = await (browser as ChromiumBrowser).newBrowserCDPSession();
const targets = []; const targets = [];

View file

@ -26,276 +26,281 @@ registerWorkerFixture('browser', async ({browserType, defaultBrowserOptions}, te
await browser.close(); await browser.close();
}); });
it.skip(!options.CHROMIUM)('should report oopif frames', async function({browser, page, server}) { describe.skip(!options.CHROMIUM)('oopif', () => {
await page.goto(server.PREFIX + '/dynamic-oopif.html'); it('should report oopif frames', async function({browser, page, server}) {
expect(await countOOPIFs(browser)).toBe(1); 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'); 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);
}); });
expect(result).toBe(36);
});
it.skip(!options.CHROMIUM)('should emulate media', async ({browser, page, server}) => { it('should handle oopif detach', async function({browser, page, server}) {
await page.goto(server.PREFIX + '/dynamic-oopif.html'); await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(page.frames().length).toBe(2); expect(await countOOPIFs(browser)).toBe(1);
expect(await countOOPIFs(browser)).toBe(1); expect(page.frames().length).toBe(2);
const oopif = page.frames()[1]; const frame = page.frames()[1];
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false); expect(await frame.evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
await page.emulateMedia({ colorScheme: 'dark' }); const [detachedFrame] = await Promise.all([
expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true); page.waitForEvent('framedetached'),
}); page.evaluate(() => document.querySelector('iframe').remove()),
]);
it.skip(!options.CHROMIUM)('should emulate offline', async ({browser, page, context, server}) => { expect(detachedFrame).toBe(frame);
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 lEst nord-américain)');
expect(await oopif.evaluate(() => navigator.language)).toBe('fr-CH');
expect(await oopif.evaluate(() => navigator.userAgent)).toBe('UA');
expect(request.headers['user-agent']).toBe('UA');
await context.close();
});
it.skip(!options.CHROMIUM)('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.skip(!options.CHROMIUM)('should take screenshot', async ({browser, page, server, golden}) => { it('should handle remote -> local -> remote transitions', async function({browser, page, server}) {
await page.setViewportSize({width: 500, height: 500}); await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.goto(server.PREFIX + '/dynamic-oopif.html'); expect(page.frames().length).toBe(2);
expect(page.frames().length).toBe(2); expect(await countOOPIFs(browser)).toBe(1);
expect(await countOOPIFs(browser)).toBe(1); expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
expect(await page.screenshot()).toMatchImage(golden('screenshot-oopif.png'), { threshold: 0.3 }); await Promise.all([
}); page.frames()[1].waitForNavigation(),
page.evaluate('goLocal()'),
it.skip(!options.CHROMIUM)('should load oopif iframes with subresources and route', async function({browser, page, server, context}) { ]);
await page.route('**/*', route => route.continue()); expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.PREFIX + '/grid.html');
await page.goto(server.PREFIX + '/dynamic-oopif.html'); expect(await countOOPIFs(browser)).toBe(0);
expect(await countOOPIFs(browser)).toBe(1); await Promise.all([
}); page.frames()[1].waitForNavigation(),
page.evaluate('goRemote()'),
it.skip(!options.CHROMIUM)('should report main requests', async function({browser, page, server}) { ]);
const requestFrames = []; expect(await page.frames()[1].evaluate(() => '' + location.href)).toBe(server.CROSS_PROCESS_PREFIX + '/grid.html');
page.on('request', r => requestFrames.push(r.frame())); expect(await countOOPIFs(browser)).toBe(1);
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'});
}); });
await page.evaluate(() => {
const frame = document.createElement('iframe'); it('should get the proper viewport', test => {
frame.setAttribute('src', 'https://google.com/'); test.fixme(options.CHROMIUM);
document.body.appendChild(frame); test.skip(!options.CHROMIUM);
return new Promise(x => frame.onload = x); }, 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}) { it('should expose function', async ({browser, page, server}) => {
await page.goto(server.PREFIX + '/dynamic-oopif.html'); await page.goto(server.PREFIX + '/dynamic-oopif.html');
await page.$eval('iframe', iframe => { expect(page.frames().length).toBe(2);
iframe.style.width = '500px'; expect(await countOOPIFs(browser)).toBe(1);
iframe.style.height = '500px'; const oopif = page.frames()[1];
iframe.style.marginLeft = '42px'; await page.exposeFunction('mul', (a, b) => a * b);
iframe.style.marginTop = '17px'; 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); it('should emulate media', async ({browser, page, server}) => {
const handle1 = await page.frames()[1].$('.box:nth-of-type(13)'); await page.goto(server.PREFIX + '/dynamic-oopif.html');
expect(await handle1.boundingBox()).toEqual({ x: 100 + 42, y: 50 + 17, width: 50, height: 50 }); expect(page.frames().length).toBe(2);
expect(await countOOPIFs(browser)).toBe(1);
await Promise.all([ const oopif = page.frames()[1];
page.frames()[1].waitForNavigation(), expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(false);
page.evaluate('goLocal()'), await page.emulateMedia({ colorScheme: 'dark' });
]); expect(await oopif.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches)).toBe(true);
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';
}); });
await page.frames()[1].goto(page.frames()[1].url());
expect(await countOOPIFs(browser)).toBe(1); it('should emulate offline', async ({browser, page, context, server}) => {
const handle1 = await page.frames()[1].$('.box:nth-of-type(13)'); await page.goto(server.PREFIX + '/dynamic-oopif.html');
await handle1.evaluate(div => div.addEventListener('click', () => window['_clicked'] = true, false)); expect(page.frames().length).toBe(2);
await handle1.click(); expect(await countOOPIFs(browser)).toBe(1);
expect(await handle1.evaluate(() => window['_clicked'])).toBe(true); 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 lEst nord-américain)');
expect(await oopif.evaluate(() => navigator.language)).toBe('fr-CH');
expect(await oopif.evaluate(() => navigator.userAgent)).toBe('UA');
expect(request.headers['user-agent']).toBe('UA');
await context.close();
});
it('should respect route', async ({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) { async function countOOPIFs(browser) {

View file

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

View file

@ -34,57 +34,59 @@ registerFixture('outputFile', async ({tmpDir}, test) => {
fs.unlinkSync(outputFile); fs.unlinkSync(outputFile);
}); });
it.skip(!options.CHROMIUM)('should output a trace', async ({browser, page, server, outputFile}) => { describe.skip(!options.CHROMIUM)('oopif', () => {
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); it('should output a trace', async ({browser, page, server, outputFile}) => {
await page.goto(server.PREFIX + '/grid.html'); await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile});
await (browser as ChromiumBrowser).stopTracing(); await page.goto(server.PREFIX + '/grid.html');
expect(fs.existsSync(outputFile)).toBe(true); 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}) => { it('should create directories as needed', async ({browser, page, server, tmpDir}) => {
const filePath = path.join(tmpDir, 'these', 'are', 'directories'); const filePath = path.join(tmpDir, 'these', 'are', 'directories');
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath}); await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: filePath});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
await (browser as ChromiumBrowser).stopTracing(); await (browser as ChromiumBrowser).stopTracing();
expect(fs.existsSync(filePath)).toBe(true); expect(fs.existsSync(filePath)).toBe(true);
}); });
it.skip(!options.CHROMIUM)('should run with custom categories if provided', async ({browser, page, outputFile}) => { 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).startTracing(page, {path: outputFile, categories: ['disabled-by-default-v8.cpu_profiler.hires']});
await (browser as ChromiumBrowser).stopTracing(); await (browser as ChromiumBrowser).stopTracing();
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString()); const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires'); 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}) => { it('should throw if tracing on two pages', async ({browser, page, outputFile}) => {
await (browser as ChromiumBrowser).startTracing(page, {path: outputFile}); await (browser as ChromiumBrowser).startTracing(page, {path: outputFile});
const newPage = await browser.newPage(); const newPage = await browser.newPage();
let error = null; let error = null;
await (browser as ChromiumBrowser).startTracing(newPage, {path: outputFile}).catch(e => error = e); await (browser as ChromiumBrowser).startTracing(newPage, {path: outputFile}).catch(e => error = e);
await newPage.close(); await newPage.close();
expect(error).toBeTruthy(); expect(error).toBeTruthy();
await (browser as ChromiumBrowser).stopTracing(); await (browser as ChromiumBrowser).stopTracing();
}); });
it.skip(!options.CHROMIUM)('should return a buffer', async ({browser, page, server, outputFile}) => { it('should return a buffer', async ({browser, page, server, outputFile}) => {
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile}); await (browser as ChromiumBrowser).startTracing(page, {screenshots: true, path: outputFile});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const trace = await (browser as ChromiumBrowser).stopTracing(); const trace = await (browser as ChromiumBrowser).stopTracing();
const buf = fs.readFileSync(outputFile); const buf = fs.readFileSync(outputFile);
expect(trace.toString()).toEqual(buf.toString()); expect(trace.toString()).toEqual(buf.toString());
}); });
it.skip(!options.CHROMIUM)('should work without options', async ({browser, page, server}) => { it('should work without options', async ({browser, page, server}) => {
await (browser as ChromiumBrowser).startTracing(page); await (browser as ChromiumBrowser).startTracing(page);
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const trace = await (browser as ChromiumBrowser).stopTracing(); const trace = await (browser as ChromiumBrowser).stopTracing();
expect(trace).toBeTruthy(); expect(trace).toBeTruthy();
}); });
it.skip(!options.CHROMIUM)('should support a buffer without a path', async ({browser, page, server}) => { it('should support a buffer without a path', async ({browser, page, server}) => {
await (browser as ChromiumBrowser).startTracing(page, {screenshots: true}); await (browser as ChromiumBrowser).startTracing(page, {screenshots: true});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const trace = await (browser as ChromiumBrowser).stopTracing(); const trace = await (browser as ChromiumBrowser).stopTracing();
expect(trace.toString()).toContain('screenshot'); expect(trace.toString()).toContain('screenshot');
});
}); });

View file

@ -20,7 +20,9 @@ declare const renderComponent;
declare const e; declare const e;
declare const MyButton; 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.goto(server.PREFIX + '/react.html');
await page.evaluate(() => { await page.evaluate(() => {
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })])); 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'); 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.goto(server.PREFIX + '/react.html');
await page.evaluate(() => { await page.evaluate(() => {
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })])); 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(); 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.goto(server.PREFIX + '/react.html');
await page.evaluate(() => { await page.evaluate(() => {
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2' })])); 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); 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.goto(server.PREFIX + '/react.html');
await page.evaluate(() => { await page.evaluate(() => {
renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })])); renderComponent(e('div', {}, [e(MyButton, { name: 'button1' }), e(MyButton, { name: 'button2', disabled: true })]));

View file

@ -17,7 +17,9 @@
import { options } from './playwright.fixtures'; 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'); 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); 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. await page.waitForTimeout(5000); // Give it some time to click after the test hook is done waiting.

View file

@ -17,7 +17,9 @@
import { options } from './playwright.fixtures'; 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('<button>Click me</button>'); await page.setContent('<button>Click me</button>');
let clicked = false; let clicked = false;
const handle = await page.$('button'); const handle = await page.$('button');

View file

@ -322,7 +322,9 @@ it('should click the button inside an iframe', async ({page, server}) => {
expect(await frame.evaluate(() => window['result'])).toBe('Clicked'); 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://github.com/GoogleChrome/puppeteer/issues/4110
// @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390 // @see https://bugs.chromium.org/p/chromium/issues/detail?id=986390
// @see https://chromium-review.googlesource.com/c/chromium/src/+/1742784 // @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); 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 context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/input/button.html'); await page.goto(server.PREFIX + '/input/button.html');

View file

@ -170,8 +170,9 @@ it('should support offline option', async ({server, launchPersistent}) => {
expect(error).toBeTruthy(); expect(error).toBeTruthy();
}); });
it.skip(true)('should support acceptDownloads option', async ({server, launchPersistent}) => { it('should support acceptDownloads option', test => {
// TODO: unskip once we support downloads in persistent context. test.skip('Unskip once we support downloads in persistent context.');
}, async ({server, launchPersistent}) => {
const {page} = await launchPersistent({acceptDownloads: true}); const {page} = await launchPersistent({acceptDownloads: true});
server.setRoute('/download', (req, res) => { server.setRoute('/download', (req, res) => {
res.setHeader('Content-Type', 'application/octet-stream'); res.setHeader('Content-Type', 'application/octet-stream');

View file

@ -26,7 +26,9 @@ it('should support hasTouch option', async ({server, launchPersistent}) => {
expect(await page.evaluate(() => 'ontouchstart' in window)).toBe(true); 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. // Firefox does not support mobile.
const {page} = await launchPersistent({viewport: {width: 320, height: 480}, isMobile: true}); const {page} = await launchPersistent({viewport: {width: 320, height: 480}, isMobile: true});
await page.goto(server.PREFIX + '/empty.html'); 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'); 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(); const {context} = await launchPersistent();
// Note: we need an open page to make sure its functional. // Note: we need an open page to make sure its functional.
expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0); expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0);
@ -85,7 +89,9 @@ it.flaky(options.CHROMIUM)('should accept userDataDir', async ({launchPersistent
await removeUserDataDir(tmpDir); 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 userDataDir = await makeUserDataDir();
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions); const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
const page = await browserContext.newPage(); const page = await browserContext.newPage();
@ -111,7 +117,10 @@ it.slow()('should restore state from userDataDir', async ({browserType, defaultB
await removeUserDataDir(userDataDir2); 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 userDataDir = await makeUserDataDir();
const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions); const browserContext = await browserType.launchPersistentContext(userDataDir, defaultBrowserOptions);
const page = await browserContext.newPage(); const page = await browserContext.newPage();
@ -147,13 +156,17 @@ it('should have default URL when launching browser', async ({launchPersistent})
expect(urls).toEqual(['about:blank']); 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 options = {...defaultBrowserOptions, args: [server.EMPTY_PAGE] };
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e); const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e);
expect(error.message).toContain('can not specify page'); 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 args = toImpl(browserType)._defaultArgs(defaultBrowserOptions, 'persistent', tmpDir, 0).filter(a => a !== 'about:blank');
const options = { const options = {
...defaultBrowserOptions, ...defaultBrowserOptions,
@ -169,13 +182,17 @@ it.skip(options.WIRE)('should have passed URL when launching with ignoreDefaultA
await browserContext.close(); 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 options = { ...defaultBrowserOptions, timeout: 5000, __testHookBeforeCreateBrowser: () => new Promise(f => setTimeout(f, 6000)) };
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e); const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => e);
expect(error.message).toContain(`browserType.launchPersistentContext: Timeout 5000ms exceeded.`); 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 e = new Error('Dummy');
const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } }; const options = { ...defaultBrowserOptions, __testHookBeforeCreateBrowser: () => { throw e; } };
const error = await browserType.launchPersistentContext(tmpDir, options).catch(e => 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); 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(); const {page} = await launchPersistent();
await page.coverage.startJSCoverage(); await page.coverage.startJSCoverage();
await page.goto(server.PREFIX + '/jscoverage/simple.html', { waitUntil: 'load' }); 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); 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(); const {page} = await launchPersistent();
expect(page.coverage).toBe(null); expect(page.coverage).toBe(null);
}); });

View file

@ -62,7 +62,9 @@ it('should dismiss the confirm prompt', async ({page}) => {
expect(result).toBe(false); 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 context = await browser.newContext();
const page = await context.newPage(); const page = await context.newPage();
const alertPromise = page.waitForEvent('dialog'); const alertPromise = page.waitForEvent('dialog');

View file

@ -126,7 +126,9 @@ it('should be atomic', async ({playwright, page}) => {
expect(await page.evaluate(() => window['_clicked'])).toBe(true); 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'); await page.goto(server.PREFIX + '/drag-n-drop.html');
const dataTransfer = await page.evaluateHandle(() => new DataTransfer()); const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
await page.dispatchEvent('#source', 'dragstart', { 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(); }, {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'); await page.goto(server.PREFIX + '/drag-n-drop.html');
const dataTransfer = await page.evaluateHandle(() => new DataTransfer()); const dataTransfer = await page.evaluateHandle(() => new DataTransfer());
const source = await page.$('#source'); const source = await page.$('#source');

View file

@ -144,7 +144,9 @@ it('should create subdirectories when saving to non-existent user-specified path
await page.close(); 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 browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
const page = await browser.newPage({ acceptDownloads: true }); const page = await browser.newPage({ acceptDownloads: true });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`); await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
@ -188,7 +190,9 @@ it('should error when saving after deletion', async ({tmpDir, browser, server})
await page.close(); 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 browser = await browserType.connect({ wsEndpoint: remoteServer.wsEndpoint() });
const page = await browser.newPage({ acceptDownloads: true }); const page = await browser.newPage({ acceptDownloads: true });
await page.setContent(`<a href="${server.PREFIX}/download">download</a>`); await page.setContent(`<a href="${server.PREFIX}/download">download</a>`);
@ -250,7 +254,9 @@ it(`should report download path within page.on('download', …) handler for Blob
expect(fs.readFileSync(path).toString()).toBe('Hello world'); expect(fs.readFileSync(path).toString()).toBe('Hello world');
await page.close(); 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. // Firefox does not download on alt-click by default.
// Our WebKit embedder does not download on alt-click, although Safari does. // Our WebKit embedder does not download on alt-click, although Safari does.
server.setRoute('/download', (req, res) => { server.setRoute('/download', (req, res) => {
@ -271,7 +277,9 @@ it.fixme(options.FIREFOX || options.WEBKIT)('should report alt-click downloads',
await page.close(); 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 // TODO: - the test fails in headful Chromium as the popup page gets closed along
// with the session before download completed event arrives. // with the session before download completed event arrives.
// - WebKit doesn't close the popup page // - WebKit doesn't close the popup page

View file

@ -20,105 +20,106 @@ import './electron.fixture';
import path from 'path'; import path from 'path';
const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron'; const electronName = process.platform === 'win32' ? 'electron.cmd' : 'electron';
describe.skip(!options.CHROMIUM)('electron app', () => {
it.skip(!options.CHROMIUM)('should fire close event', async ({ playwright }) => { it('should fire close event', async ({ playwright }) => {
const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName); const electronPath = path.join(__dirname, '..', '..', 'node_modules', '.bin', electronName);
const application = await playwright.electron.launch(electronPath, { const application = await playwright.electron.launch(electronPath, {
args: [path.join(__dirname, 'testApp.js')], 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 }) => { it('should script application', async ({ application }) => {
const appPath = await application.evaluate(async ({ app }) => app.getAppPath()); const appPath = await application.evaluate(async ({ app }) => app.getAppPath());
expect(appPath).toContain('electron'); expect(appPath).toContain('electron');
}); });
it.skip(!options.CHROMIUM)('should create window', async ({ application }) => { it('should create window', async ({ application }) => {
const [ page ] = await Promise.all([ const [ page ] = await Promise.all([
application.waitForEvent('window'), application.waitForEvent('window'),
application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World 1</title>');
})
]);
await page.waitForLoadState('domcontentloaded');
expect(await page.title()).toBe('Hello World 1');
});
it('should create window 2', async ({ application }) => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<title>Hello World 2</title>');
expect(await page.title()).toBe('Hello World 2');
});
it('should create multiple windows', async ({ application }) => {
const createPage = async ordinal => {
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await Promise.all([
page.waitForNavigation(),
page.browserWindow.evaluate((window, ordinal) => window.loadURL(`data:text/html,<title>Hello World ${ordinal}</title>`), ordinal)
]);
return page;
};
const page1 = await createPage(1);
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: '<title>Hello World</title>',
});
});
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('https://localhost:1000/empty.html');
expect(await page.title()).toBe('Hello World');
});
it('should support init script', async ({ application }) => {
await application.context().addInitScript('window.magic = 42;');
const page = await application.newBrowserWindow({ width: 800, height: 600 });
await page.goto('data:text/html,<script>window.copy = magic</script>');
expect(await page.evaluate(() => 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,<script>window["result"] = add(20, 22);</script>');
expect(await page.evaluate(() => window['result'])).toBe(42);
});
it('should wait for first window', async ({ application }) => {
application.evaluate(({ BrowserWindow }) => { application.evaluate(({ BrowserWindow }) => {
const window = new BrowserWindow({ width: 800, height: 600 }); const window = new BrowserWindow({ width: 800, height: 600 });
window.loadURL('data:text/html,<title>Hello World 1</title>'); window.loadURL('data:text/html,<title>Hello World!</title>');
})
]);
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,<title>Hello World 2</title>');
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,<title>Hello World ${ordinal}</title>`), 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: '<title>Hello World</title>',
}); });
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 }) => { it('should have a clipboard instance', async ({ application }) => {
await application.context().addInitScript('window.magic = 42;'); const clipboardContentToWrite = 'Hello from Playwright';
const page = await application.newBrowserWindow({ width: 800, height: 600 }); await application.evaluate(async ({clipboard}, text) => clipboard.writeText(text), clipboardContentToWrite);
await page.goto('data:text/html,<script>window.copy = magic</script>'); const clipboardContentRead = await application.evaluate(async ({clipboard}) => clipboard.readText());
expect(await page.evaluate(() => window['copy'])).toBe(42); await expect(clipboardContentRead).toEqual(clipboardContentToWrite);
});
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,<script>window["result"] = add(20, 22);</script>');
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,<title>Hello World!</title>');
}); });
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);
}); });

View file

@ -17,31 +17,33 @@
import { options } from '../playwright.fixtures'; import { options } from '../playwright.fixtures';
import './electron.fixture'; import './electron.fixture';
it.skip(!options.CHROMIUM)('should click the button', async ({window, server}) => { describe.skip(!options.CHROMIUM)('electron window', () => {
await window.goto(server.PREFIX + '/input/button.html'); it('should click the button', async ({window, server}) => {
await window.click('button'); await window.goto(server.PREFIX + '/input/button.html');
expect(await window.evaluate('result')).toBe('Clicked'); await window.click('button');
}); expect(await window.evaluate('result')).toBe('Clicked');
});
it.skip(!options.CHROMIUM)('should check the box', async ({window}) => {
await window.setContent(`<input id='checkbox' type='checkbox'></input>`); it('should check the box', async ({window}) => {
await window.check('input'); await window.setContent(`<input id='checkbox' type='checkbox'></input>`);
expect(await window.evaluate('checkbox.checked')).toBe(true); 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(`<input id='checkbox' type='checkbox' checked></input>`); it('should not check the checked box', async ({window}) => {
await window.check('input'); await window.setContent(`<input id='checkbox' type='checkbox' checked></input>`);
expect(await window.evaluate('checkbox.checked')).toBe(true); 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(() => { it('should type into a textarea', async ({window, server}) => {
const textarea = document.createElement('textarea'); await window.evaluate(() => {
document.body.appendChild(textarea); const textarea = document.createElement('textarea');
textarea.focus(); 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);
}); });

View file

@ -17,7 +17,9 @@
import { options } from './playwright.fixtures'; 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.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const elementHandle = await page.$('.box:nth-of-type(13)'); 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); 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 context = await browser.newContext({ viewport: { width: 400, height: 400 }, isMobile: true });
const page = await context.newPage(); const page = await context.newPage();
await page.goto(server.PREFIX + '/input/button.html'); await page.goto(server.PREFIX + '/input/button.html');

View file

@ -35,7 +35,9 @@ it('should work for cross-process iframes', async ({ page, server }) => {
expect(await elementHandle.ownerFrame()).toBe(frame); 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 page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE); await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1]; const frame = page.frames()[1];

View file

@ -24,359 +24,373 @@ import fs from 'fs';
// Firefox headful produces a different image. // Firefox headful produces a different image.
const ffheadful = options.FIREFOX && !options.HEADLESS; const ffheadful = options.FIREFOX && !options.HEADLESS;
it.skip(ffheadful)('should work', async ({page, server, golden}) => { describe.skip(ffheadful)('element screenshot', () => {
await page.setViewportSize({width: 500, height: 500}); it('should work', async ({page, server, golden}) => {
await page.goto(server.PREFIX + '/grid.html'); await page.setViewportSize({width: 500, height: 500});
await page.evaluate(() => window.scrollBy(50, 100)); await page.goto(server.PREFIX + '/grid.html');
const elementHandle = await page.$('.box:nth-of-type(3)'); await page.evaluate(() => window.scrollBy(50, 100));
const screenshot = await elementHandle.screenshot(); const elementHandle = await page.$('.box:nth-of-type(3)');
expect(screenshot).toMatchImage(golden('screenshot-element-bounding-box.png')); 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(`
<div style="height: 14px">oooo</div>
<style>div {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div id="d"></div>
`);
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(`
<div style="height: 14px">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
`);
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(`
<div style="height: 14px">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
`);
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(`
<div style="height: 14px">oooo</div>
<style>div.above {
border: 2px solid blue;
background: red;
height: 1500px;
}
div.to-screenshot {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div class="above"></div>
<div class="to-screenshot"></div>
`);
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(`
<div style="height: 14px">oooo</div>
<style>div.above {
border: 2px solid blue;
background: red;
height: 15000px;
}
div.to-screenshot {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div class="above"></div>
<div class="to-screenshot"></div>
`);
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(`<div style="position:absolute;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
background: green;
transform: rotateZ(200deg);">&nbsp;</div>`);
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('<h1>remove this</h1>');
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('<div style="width: 50px; height: 0"></div>');
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;
}); });
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}) => { it('should take into account padding and border', async ({page, golden}) => {
await page.setContent('<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>'); await page.setViewportSize({width: 500, height: 500});
const elementHandle = await page.$('div'); await page.setContent(`
const screenshot = await elementHandle.screenshot(); <div style="height: 14px">oooo</div>
expect(screenshot).toMatchImage(golden('screenshot-element-fractional.png')); <style>div {
}); border: 2px solid blue;
background: green;
it.skip(options.FIREFOX)('should work with a mobile viewport', async ({browser, server, golden}) => { width: 50px;
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); height: 50px;
const page = await context.newPage(); }
await page.goto(server.PREFIX + '/grid.html'); </style>
await page.evaluate(() => window.scrollBy(50, 100)); <div id="d"></div>
const elementHandle = await page.$('.box:nth-of-type(3)'); `);
const screenshot = await elementHandle.screenshot(); const elementHandle = await page.$('div#d');
expect(screenshot).toMatchImage(golden('screenshot-element-mobile.png')); const screenshot = await elementHandle.screenshot();
await context.close(); expect(screenshot).toMatchImage(golden('screenshot-element-padding-border.png'));
});
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('<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>');
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(`<div style='height: 10000px; background: red'></div>`);
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
}); });
expect(screenshot).toBeInstanceOf(Buffer);
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight })); it('should capture full element when larger than viewport in parallel', async ({page, golden}) => {
expect(sizeBefore.width).toBe(sizeAfter.width); await page.setViewportSize({width: 500, height: 500});
expect(sizeBefore.height).toBe(sizeAfter.height);
await context.close();
});
it.skip(ffheadful)('should restore default viewport after fullPage screenshot', async ({ browser }) => { await page.setContent(`
const context = await browser.newContext({ viewport: { width: 456, height: 789 } }); <div style="height: 14px">oooo</div>
const page = await context.newPage(); <style>
await utils.verifyViewport(page, 456, 789); div.to-screenshot {
const screenshot = await page.screenshot({ fullPage: true }); border: 1px solid blue;
expect(screenshot).toBeInstanceOf(Buffer); width: 600px;
await utils.verifyViewport(page, 456, 789); height: 600px;
await context.close(); margin-left: 50px;
}); }
::-webkit-scrollbar{
display: none;
}
</style>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
`);
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 }) => { await utils.verifyViewport(page, 500, 500);
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(`
<div style="height: 14px">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
`);
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(`<div style="width:600px;height:600px;"></div>`);
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)));
}); });
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}) => { it('should capture full element when larger than viewport', async ({page, golden}) => {
await page.setViewportSize({ width: 500, height: 500 }); await page.setViewportSize({width: 500, height: 500});
await page.setContent(`<button disabled>Click me</button>`);
const button = await page.$('button');
const screenshot = await button.screenshot();
expect(screenshot).toBeInstanceOf(Buffer);
});
it.skip(ffheadful)('path option should create subdirectories', async ({page, server, golden, tmpDir}) => { await page.setContent(`
await page.setViewportSize({width: 500, height: 500}); <div style="height: 14px">oooo</div>
await page.goto(server.PREFIX + '/grid.html'); <style>
await page.evaluate(() => window.scrollBy(50, 100)); div.to-screenshot {
const elementHandle = await page.$('.box:nth-of-type(3)'); border: 1px solid blue;
const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png'); width: 600px;
await elementHandle.screenshot({path: outputPath}); height: 600px;
expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-element-bounding-box.png')); margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
`);
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(`
<div style="height: 14px">oooo</div>
<style>div.above {
border: 2px solid blue;
background: red;
height: 1500px;
}
div.to-screenshot {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div class="above"></div>
<div class="to-screenshot"></div>
`);
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(`
<div style="height: 14px">oooo</div>
<style>div.above {
border: 2px solid blue;
background: red;
height: 15000px;
}
div.to-screenshot {
border: 2px solid blue;
background: green;
width: 50px;
height: 50px;
}
</style>
<div class="above"></div>
<div class="to-screenshot"></div>
`);
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(`<div style="position:absolute;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
background: green;
transform: rotateZ(200deg);">&nbsp;</div>`);
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('<h1>remove this</h1>');
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('<div style="width: 50px; height: 0"></div>');
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('<div style="width:48.51px;height:19.8px;border:1px solid black;"></div>');
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('<div style="position:absolute; top: 10.3px; left: 20.4px;width:50.3px;height:20.2px;border:1px solid black;"></div>');
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(`<div style='height: 10000px; background: red'></div>`);
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(`
<div style="height: 14px">oooo</div>
<style>
div.to-screenshot {
border: 1px solid blue;
width: 600px;
height: 600px;
margin-left: 50px;
}
::-webkit-scrollbar{
display: none;
}
</style>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
<div class="to-screenshot"></div>
`);
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(`<div style="width:600px;height:600px;"></div>`);
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(`<button disabled>Click me</button>`);
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'));
});
}); });

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import './playwright.fixtures'; import { options } from './playwright.fixtures';
async function giveItAChanceToResolve(page) { async function giveItAChanceToResolve(page) {
for (let i = 0; i < 5; i++) for (let i = 0; i < 5; i++)
@ -114,7 +114,9 @@ it('should wait for disabled button', async ({page}) => {
await promise; 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'); await page.goto(server.PREFIX + '/input/button.html');
const button = await page.$('button'); const button = await page.$('button');
await page.$eval('button', button => { await page.$eval('button', button => {

View file

@ -101,7 +101,9 @@ it('should change document.activeElement', async ({page, server}) => {
expect(active).toEqual(['INPUT', 'TEXTAREA']); 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. // Firefox headful produces a different image.
const page2 = await page.context().newPage(); const page2 = await page.context().newPage();
await Promise.all([ await Promise.all([

View file

@ -15,7 +15,9 @@
*/ */
import { options } from '../playwright.fixtures'; 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({ const browser = await browserType.launch({
...defaultBrowserOptions, ...defaultBrowserOptions,
firefoxUserPrefs: { firefoxUserPrefs: {

View file

@ -20,7 +20,9 @@ import './remoteServer.fixture';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import path from 'path'; 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) if (WIN)
execSync(`taskkill /pid ${remoteServer.child().pid} /T /F`); execSync(`taskkill /pid ${remoteServer.child().pid} /T /F`);
else 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. // so we don't check it here.
}); });
// Cannot reliably send signals on Windows. describe.skip(WIN || !options.HEADLESS).slow()('fixtures', () => {
it.skip(WIN || !options.HEADLESS).slow()('should report browser close signal', async ({remoteServer}) => { // Cannot reliably send signals on Windows.
const pid = await remoteServer.out('pid'); it('should report browser close signal', async ({remoteServer}) => {
process.kill(-pid, 'SIGTERM'); const pid = await remoteServer.out('pid');
expect(await remoteServer.out('exitCode')).toBe('null'); process.kill(-pid, 'SIGTERM');
expect(await remoteServer.out('signal')).toBe('SIGTERM'); expect(await remoteServer.out('exitCode')).toBe('null');
process.kill(remoteServer.child().pid); expect(await remoteServer.out('signal')).toBe('SIGTERM');
await remoteServer.childExitCode(); process.kill(remoteServer.child().pid);
}); await remoteServer.childExitCode();
});
it.skip(WIN || !options.HEADLESS).slow()('should report browser close signal 2', async ({remoteServer}) => { it('should report browser close signal 2', async ({remoteServer}) => {
const pid = await remoteServer.out('pid'); const pid = await remoteServer.out('pid');
process.kill(-pid, 'SIGKILL'); process.kill(-pid, 'SIGKILL');
expect(await remoteServer.out('exitCode')).toBe('null'); expect(await remoteServer.out('exitCode')).toBe('null');
expect(await remoteServer.out('signal')).toBe('SIGKILL'); expect(await remoteServer.out('signal')).toBe('SIGKILL');
process.kill(remoteServer.child().pid); process.kill(remoteServer.child().pid);
await remoteServer.childExitCode(); await remoteServer.childExitCode();
}); });
it.skip(WIN || !options.HEADLESS).slow()('should close the browser on SIGINT', async ({remoteServer}) => { it('should close the browser on SIGINT', async ({remoteServer}) => {
process.kill(remoteServer.child().pid, 'SIGINT'); process.kill(remoteServer.child().pid, 'SIGINT');
expect(await remoteServer.out('exitCode')).toBe('0'); expect(await remoteServer.out('exitCode')).toBe('0');
expect(await remoteServer.out('signal')).toBe('null'); expect(await remoteServer.out('signal')).toBe('null');
expect(await remoteServer.childExitCode()).toBe(130); expect(await remoteServer.childExitCode()).toBe(130);
}); });
it.skip(WIN || !options.HEADLESS).slow()('should close the browser on SIGTERM', async ({remoteServer}) => { it('should close the browser on SIGTERM', async ({remoteServer}) => {
process.kill(remoteServer.child().pid, 'SIGTERM'); process.kill(remoteServer.child().pid, 'SIGTERM');
expect(await remoteServer.out('exitCode')).toBe('0'); expect(await remoteServer.out('exitCode')).toBe('0');
expect(await remoteServer.out('signal')).toBe('null'); expect(await remoteServer.out('signal')).toBe('null');
expect(await remoteServer.childExitCode()).toBe(0); expect(await remoteServer.childExitCode()).toBe(0);
}); });
it.skip(WIN || !options.HEADLESS).slow()('should close the browser on SIGHUP', async ({remoteServer}) => { it('should close the browser on SIGHUP', async ({remoteServer}) => {
process.kill(remoteServer.child().pid, 'SIGHUP'); process.kill(remoteServer.child().pid, 'SIGHUP');
expect(await remoteServer.out('exitCode')).toBe('0'); expect(await remoteServer.out('exitCode')).toBe('0');
expect(await remoteServer.out('signal')).toBe('null'); expect(await remoteServer.out('signal')).toBe('null');
expect(await remoteServer.childExitCode()).toBe(0); expect(await remoteServer.childExitCode()).toBe(0);
}); });
it.skip(WIN || !options.HEADLESS).slow()('should kill the browser on double SIGINT', async ({stallingRemoteServer}) => { it('should kill the browser on double SIGINT', async ({stallingRemoteServer}) => {
const remoteServer = stallingRemoteServer; const remoteServer = stallingRemoteServer;
process.kill(remoteServer.child().pid, 'SIGINT'); process.kill(remoteServer.child().pid, 'SIGINT');
await remoteServer.out('stalled'); await remoteServer.out('stalled');
process.kill(remoteServer.child().pid, 'SIGINT'); process.kill(remoteServer.child().pid, 'SIGINT');
expect(await remoteServer.out('exitCode')).toBe('null'); expect(await remoteServer.out('exitCode')).toBe('null');
expect(await remoteServer.out('signal')).toBe('SIGKILL'); expect(await remoteServer.out('signal')).toBe('SIGKILL');
expect(await remoteServer.childExitCode()).toBe(130); expect(await remoteServer.childExitCode()).toBe(130);
}); });
it.skip(WIN || !options.HEADLESS).slow()('should kill the browser on SIGINT + SIGTERM', async ({stallingRemoteServer}) => { it('should kill the browser on SIGINT + SIGTERM', async ({stallingRemoteServer}) => {
const remoteServer = stallingRemoteServer; const remoteServer = stallingRemoteServer;
process.kill(remoteServer.child().pid, 'SIGINT'); process.kill(remoteServer.child().pid, 'SIGINT');
await remoteServer.out('stalled'); await remoteServer.out('stalled');
process.kill(remoteServer.child().pid, 'SIGTERM'); process.kill(remoteServer.child().pid, 'SIGTERM');
expect(await remoteServer.out('exitCode')).toBe('null'); expect(await remoteServer.out('exitCode')).toBe('null');
expect(await remoteServer.out('signal')).toBe('SIGKILL'); expect(await remoteServer.out('signal')).toBe('SIGKILL');
expect(await remoteServer.childExitCode()).toBe(0); expect(await remoteServer.childExitCode()).toBe(0);
}); });
it.skip(WIN || !options.HEADLESS).slow()('should kill the browser on SIGTERM + SIGINT', async ({stallingRemoteServer}) => { it('should kill the browser on SIGTERM + SIGINT', async ({stallingRemoteServer}) => {
const remoteServer = stallingRemoteServer; const remoteServer = stallingRemoteServer;
process.kill(remoteServer.child().pid, 'SIGTERM'); process.kill(remoteServer.child().pid, 'SIGTERM');
await remoteServer.out('stalled'); await remoteServer.out('stalled');
process.kill(remoteServer.child().pid, 'SIGINT'); process.kill(remoteServer.child().pid, 'SIGINT');
expect(await remoteServer.out('exitCode')).toBe('null'); expect(await remoteServer.out('exitCode')).toBe('null');
expect(await remoteServer.out('signal')).toBe('SIGKILL'); expect(await remoteServer.out('signal')).toBe('SIGKILL');
expect(await remoteServer.childExitCode()).toBe(130); expect(await remoteServer.childExitCode()).toBe(130);
});
}); });
it('caller file path', async ({}) => { it('caller file path', async ({}) => {

View file

@ -15,7 +15,9 @@
*/ */
import { options } from './playwright.fixtures'; 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(`<div id=d1 tabIndex=0></div>`); await page.setContent(`<div id=d1 tabIndex=0></div>`);
expect(await page.evaluate(() => document.activeElement.nodeName)).toBe('BODY'); expect(await page.evaluate(() => document.activeElement.nodeName)).toBe('BODY');
await page.focus('#d1'); 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'); expect(await page.evaluate(() => (document.activeElement as HTMLInputElement).value)).toBe('1');
}); });
// Chromium and WebKit both have settings for tab traversing all links, but it('should traverse only form elements', test => {
// it is only on by default in WebKit. test.skip(!MAC || !options.WEBKIT,
it.skip(!MAC || !options.WEBKIT)('should traverse only form elements', async function({page}) { '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(` await page.setContent(`
<input id="input-1"> <input id="input-1">
<button id="button">buttton</button> <button id="button">buttton</button>

View file

@ -42,7 +42,9 @@ function expectContexts(pageImpl, count) {
expect(pageImpl._delegate._contextIdToContext.size).toBe(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'); await page.goto(server.PREFIX + '/frames/one-frame.html');
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
expectContexts(toImpl(page), 4); expectContexts(toImpl(page), 4);
@ -50,7 +52,9 @@ it.skip(options.WIRE)('should dispose context on navigation', async ({ page, ser
expectContexts(toImpl(page), 2); 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'); await page.goto(server.PREFIX + '/frames/one-frame.html');
expect(page.frames().length).toBe(2); expect(page.frames().length).toBe(2);
expectContexts(toImpl(page), 4); expectContexts(toImpl(page), 4);
@ -126,7 +130,9 @@ it('should be isolated between frames', async ({page, server}) => {
expect(a2).toBe(2); 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. // - Firefox does not report domcontentloaded for the iframe.
// - Chromium and Firefox report empty url. // - Chromium and Firefox report empty url.
// - Chromium does not report main/utility worlds for the iframe. // - 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(); 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. // Chromium does not report isolated world for the iframe.
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => { await page.evaluate(() => {

View file

@ -171,7 +171,9 @@ it('should report different frame instance when frame re-attaches', async ({page
expect(frame1).not.toBe(frame2); 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) => { server.setRoute('/x-frame-options-deny.html', async (req, res) => {
res.setHeader('Content-Type', 'text/html'); res.setHeader('Content-Type', 'text/html');
res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('X-Frame-Options', 'DENY');

View file

@ -26,7 +26,11 @@ it('should have default url when launching browser', async ({browserType, defaul
await browserContext.close(); 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 // see https://github.com/microsoft/playwright/issues/717
const userDataDir = await makeUserDataDir(); const userDataDir = await makeUserDataDir();
// Write a cookie in headful chrome // 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'); 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 browserContext = await browserType.launchPersistentContext(tmpDir, {...defaultBrowserOptions, headless: false});
const page = await browserContext.newPage(); const page = await browserContext.newPage();
await page.goto(server.PREFIX + '/beforeunload.html'); await page.goto(server.PREFIX + '/beforeunload.html');
@ -128,7 +134,9 @@ it('should(not) block third party cookies', async ({browserType, defaultBrowserO
await browser.close(); 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. // Our WebKit embedder does not respect window features.
const browser = await browserType.launch({...defaultBrowserOptions, headless: false }); const browser = await browserType.launch({...defaultBrowserOptions, headless: false });
const context = await browser.newContext({ viewport: null }); const context = await browser.newContext({ viewport: null });

View file

@ -82,7 +82,9 @@ it('insertText should only emit input event', async ({page, server}) => {
expect(await events.jsonValue()).toEqual(['input']); 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'); await page.goto(server.PREFIX + '/input/keyboard.html');
const keyboard = page.keyboard; const keyboard = page.keyboard;
const codeForKey = {'Shift': 16, 'Alt': 18, 'Control': 17}; 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'); 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'); await page.goto(server.PREFIX + '/input/textarea.html');
const textarea = await page.$('textarea'); const textarea = await page.$('textarea');
await textarea.type('some text'); 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 // 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 lastEvent = await captureLastKeydown(page);
const keyMap = { const keyMap = {
'ArrowUp': 'Up', 'ArrowUp': 'Up',

View file

@ -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 // Occasionally times out on options.FIREFOX on Windows: https://github.com/microsoft/playwright/pull/1911/checks?check_run_id=607149016
await page.evaluate(() => { await page.evaluate(() => {
window['clickPromise'] = new Promise(resolve => { 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 // @see https://crbug.com/929806
const context = await browser.newContext({ viewport: {width: 360, height: 640}, isMobile: true }); const context = await browser.newContext({ viewport: {width: 360, height: 640}, isMobile: true });
const page = await context.newPage(); const page = await context.newPage();

View file

@ -55,7 +55,9 @@ it('should return headers', async ({page, server}) => {
expect(response.request().headers()['user-agent']).toContain('WebKit'); 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'); await page.goto(server.PREFIX + '/empty.html');
let serverRequest; let serverRequest;
server.setRoute('/something', (request, response) => { server.setRoute('/something', (request, response) => {

View file

@ -74,7 +74,9 @@ it('should work with a path', async ({page, server}) => {
expect(await page.evaluate(() => window['__injected'])).toBe(42); 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.goto(server.EMPTY_PAGE);
await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') }); await page.addScriptTag({ path: path.join(__dirname, 'assets/injectedfile.js') });
const result = await page.evaluate(() => window['__injectedError'].stack); const result = await page.evaluate(() => window['__injectedError'].stack);

View file

@ -244,7 +244,9 @@ it('frame.press should work', async ({page, server}) => {
expect(await frame.evaluate(() => document.querySelector('textarea').value)).toBe('a'); 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 page1 = await context.newPage();
const page2 = await context.newPage(); const page2 = await context.newPage();
for (const page of [page1, page2]) { for (const page of [page1, page2]) {

View file

@ -415,7 +415,9 @@ it('should not throw an error when evaluation does a navigation', async ({ page,
expect(result).toEqual([42]); 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. // It is imporant to be on about:blank for sync reload.
const result = await page.evaluate(() => { const result = await page.evaluate(() => {
window.location.reload(); window.location.reload();

View file

@ -26,42 +26,47 @@ function crash(pageImpl, browserName) {
pageImpl._delegate._session.send('Page.crash', {}).catch(e => {}); 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}) => { describe.fixme(options.WIRE).flaky(options.FIREFOX && WIN)('', () => {
await page.setContent(`<div>This page should crash</div>`); it('should emit crash event when page crashes', async ({page, browserName, toImpl}) => {
crash(toImpl(page), browserName); await page.setContent(`<div>This page should crash</div>`);
await new Promise(f => page.on('crash', f)); 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}) => { it('should throw on any action after page crashes', async ({page, browserName, toImpl}) => {
await page.setContent(`<div>This page should crash</div>`); await page.setContent(`<div>This page should crash</div>`);
crash(toImpl(page), browserName); crash(toImpl(page), browserName);
await page.waitForEvent('crash'); await page.waitForEvent('crash');
const err = await page.evaluate(() => {}).then(() => null, e => e); const err = await page.evaluate(() => {}).then(() => null, e => e);
expect(err).toBeTruthy(); expect(err).toBeTruthy();
expect(err.message).toContain('crash'); expect(err.message).toContain('crash');
}); });
it.fail(options.WIRE).flaky(options.FIREFOX && WIN)('should cancel waitForEvent when page crashes', async ({page, browserName, toImpl}) => { it('should cancel waitForEvent when page crashes', async ({page, browserName, toImpl}) => {
await page.setContent(`<div>This page should crash</div>`); await page.setContent(`<div>This page should crash</div>`);
const promise = page.waitForEvent('response').catch(e => e); const promise = page.waitForEvent('response').catch(e => e);
crash(toImpl(page), browserName); crash(toImpl(page), browserName);
const error = await promise; const error = await promise;
expect(error.message).toContain('Page crashed'); 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}) => { it('should cancel navigation when page crashes', async ({page, browserName, toImpl, server}) => {
await page.setContent(`<div>This page should crash</div>`); await page.setContent(`<div>This page should crash</div>`);
server.setRoute('/one-style.css', () => {}); server.setRoute('/one-style.css', () => {});
const promise = page.goto(server.PREFIX + '/one-style.html').catch(e => e); const promise = page.goto(server.PREFIX + '/one-style.html').catch(e => e);
await page.waitForNavigation({ waitUntil: 'domcontentloaded' }); await page.waitForNavigation({ waitUntil: 'domcontentloaded' });
crash(toImpl(page), browserName); crash(toImpl(page), browserName);
const error = await promise; const error = await promise;
expect(error.message).toContain('Navigation failed because page crashed'); 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}) => { it('should be able to close context when page crashes', test => {
await page.setContent(`<div>This page should crash</div>`); test.fixme(options.WIRE);
crash(toImpl(page), browserName); test.flaky(options.FIREFOX && WIN);
await page.waitForEvent('crash'); }, async ({page, browserName, toImpl}) => {
await page.context().close(); await page.setContent(`<div>This page should crash</div>`);
crash(toImpl(page), browserName);
await page.waitForEvent('crash');
await page.context().close();
});
}); });

View file

@ -31,7 +31,9 @@ it('should fire', async ({page, server}) => {
expect(error.stack).toBe(stack); 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([ const [error] = await Promise.all([
page.waitForEvent('pageerror'), page.waitForEvent('pageerror'),
page.goto(server.PREFIX + '/error.html'), 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. // Firefox just does not report this error.
const [error] = await Promise.all([ const [error] = await Promise.all([
page.waitForEvent('pageerror'), 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]'); 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. // Firefox just does not report this error.
const [error] = await Promise.all([ const [error] = await Promise.all([
page.waitForEvent('pageerror'), page.waitForEvent('pageerror'),

View file

@ -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'); 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('<input type=date>'); await page.setContent('<input type=date>');
const error = await page.fill('input', '2020-13-05').catch(e => e); const error = await page.fill('input', '2020-13-05').catch(e => e);
expect(error.message).toContain('Malformed value'); 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'); 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('<input type=time>'); await page.setContent('<input type=time>');
const error = await page.fill('input', '25:05').catch(e => e); const error = await page.fill('input', '25:05').catch(e => e);
expect(error.message).toContain('Malformed value'); 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'); 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('<input type=datetime-local>'); await page.setContent('<input type=datetime-local>');
const error = await page.fill('input', 'abc').catch(e => e); const error = await page.fill('input', 'abc').catch(e => e);
expect(error.message).toContain('Malformed value'); expect(error.message).toContain('Malformed value');

View file

@ -474,9 +474,10 @@ it('should fail when canceled by another navigation', async ({page, server}) =>
expect(error.message).toBeTruthy(); expect(error.message).toBeTruthy();
}); });
it.skip(true)('extraHttpHeaders should be pushed to provisional page', async ({page, server}) => { it('extraHttpHeaders should be pushed to provisional page', test => {
// This test is flaky, because we cannot await page.setExtraHTTPHeaders. 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. // We need a way to test our implementation by more than just public api.
}, async ({page, server}) => {
await page.goto(server.EMPTY_PAGE); await page.goto(server.EMPTY_PAGE);
const pagePath = '/one-style.html'; const pagePath = '/one-style.html';
server.setRoute(pagePath, async (req, res) => { server.setRoute(pagePath, async (req, res) => {

View file

@ -52,7 +52,9 @@ it('page.goBack should work with HistoryAPI', async ({page, server}) => {
expect(page.url()).toBe(server.PREFIX + '/first.html'); 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. // WebKit embedder fails to go back/forward to the file url.
const url1 = url.pathToFileURL(asset('empty.html')).href; const url1 = url.pathToFileURL(asset('empty.html')).href;
const url2 = server.EMPTY_PAGE; const url2 = server.EMPTY_PAGE;

View file

@ -23,258 +23,271 @@ import fs from 'fs';
// Firefox headful produces a different image. // Firefox headful produces a different image.
const ffheadful = options.FIREFOX && !options.HEADLESS; const ffheadful = options.FIREFOX && !options.HEADLESS;
it.skip(ffheadful)('should work', async ({page, server, golden}) => { describe.skip(ffheadful)('page screenshot', () => {
await page.setViewportSize({width: 500, height: 500}); it('should work', async ({page, server, golden}) => {
await page.goto(server.PREFIX + '/grid.html'); await page.setViewportSize({width: 500, height: 500});
const screenshot = await page.screenshot(); await page.goto(server.PREFIX + '/grid.html');
expect(screenshot).toMatchImage(golden('screenshot-sanity.png')); 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
}
}); });
expect(screenshot).toMatchImage(golden('screenshot-clip-rect.png'));
});
it.skip(ffheadful)('should clip rect with fullPage', async ({page, server, golden}) => { it('should clip rect', async ({page, server, golden}) => {
await page.setViewportSize({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(150, 200)); const screenshot = await page.screenshot({
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({
clip: { clip: {
x: 50 * i, x: 50,
y: 0, y: 100,
width: 50, width: 150,
height: 50 height: 100
} }
})); });
} expect(screenshot).toMatchImage(golden('screenshot-clip-rect.png'));
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-grid-fullpage.png'));
});
it.skip(ffheadful)('should restore viewport after fullPage screenshot', async ({page, server}) => { it('should clip rect with fullPage', async ({page, server, golden}) => {
await page.setViewportSize({width: 500, height: 500}); await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ fullPage: true }); await page.evaluate(() => window.scrollBy(150, 200));
expect(screenshot).toBeInstanceOf(Buffer); const screenshot = await page.screenshot({
await utils.verifyViewport(page, 500, 500); 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}) => { it('should clip elements to the viewport', async ({page, server, golden}) => {
const N = 5; await page.setViewportSize({width: 500, height: 500});
const pages = await Promise.all(Array(N).fill(0).map(async () => { 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(`
<style>
body { margin: 0 }
div { width: 50px; height: 50px; }
</style>
<div style="background:black"></div>
<div style="background:white"></div>
<div style="background:transparent"></div>
`);
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(); const page = await context.newPage();
await page.goto(server.PREFIX + '/grid.html'); await page.goto(server.PREFIX + '/grid.html');
return page; const screenshot = await page.screenshot();
})); expect(screenshot).toMatchImage(golden('screenshot-device-scale-factor.png'));
const promises = []; await context.close();
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(`
<style>
body { margin: 0 }
div { width: 50px; height: 50px; }
</style>
<div style="background:black"></div>
<div style="background:white"></div>
<div style="background:transparent"></div>
`);
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,
}
}); });
expect(screenshot).toMatchImage(golden('screenshot-clip-odd-size.png'));
});
it.skip(options.FIREFOX)('should work with a mobile viewport', async ({browser, server, golden}) => { it('should work with iframe in shadow', async ({page, server, golden}) => {
const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: true }); await page.setViewportSize({width: 500, height: 500});
const page = await context.newPage(); await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html');
await page.goto(server.PREFIX + '/overflow.html'); expect(await page.screenshot()).toMatchImage(golden('screenshot-iframe.png'));
const screenshot = await page.screenshot(); });
expect(screenshot).toMatchImage(golden('screenshot-mobile.png'));
await context.close();
});
it.skip(options.FIREFOX)('should work with a mobile viewport and clip', async ({browser, server, golden}) => { it('path option should work', async ({page, server, golden, tmpDir}) => {
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); await page.setViewportSize({width: 500, height: 500});
const page = await context.newPage(); await page.goto(server.PREFIX + '/grid.html');
await page.goto(server.PREFIX + '/overflow.html'); const outputPath = path.join(tmpDir, 'screenshot.png');
const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } }); await page.screenshot({path: outputPath});
expect(screenshot).toMatchImage(golden('screenshot-mobile-clip.png')); expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png'));
await context.close(); });
});
it.skip(options.FIREFOX)('should work with a mobile viewport and fullPage', async ({browser, server, golden}) => { it('path option should create subdirectories', async ({page, server, golden, tmpDir}) => {
const context = await browser.newContext({viewport: { width: 320, height: 480 }, isMobile: true}); await page.setViewportSize({width: 500, height: 500});
const page = await context.newPage(); await page.goto(server.PREFIX + '/grid.html');
await page.goto(server.PREFIX + '/overflow-large.html'); const outputPath = path.join(tmpDir, 'these', 'are', 'directories', 'screenshot.png');
const screenshot = await page.screenshot({ fullPage: true }); await page.screenshot({path: outputPath});
expect(screenshot).toMatchImage(golden('screenshot-mobile-fullpage.png')); expect(await fs.promises.readFile(outputPath)).toMatchImage(golden('screenshot-sanity.png'));
await context.close(); });
});
it.skip(ffheadful)('should work for canvas', async ({page, server, golden}) => { it('path option should detect jpeg', async ({page, server, golden, tmpDir}) => {
await page.setViewportSize({width: 500, height: 500}); await page.setViewportSize({ width: 100, height: 100 });
await page.goto(server.PREFIX + '/screenshots/canvas.html'); await page.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot(); const outputPath = path.join(tmpDir, 'screenshot.jpg');
expect(screenshot).toMatchImage(golden('screenshot-canvas.png'), { threshold: 0.3 }); 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}) => { it('path option should throw for unsupported mime type', async ({page, server, golden, tmpDir}) => {
await page.setViewportSize({width: 500, height: 500}); const error = await page.screenshot({ path: 'file.txt' }).catch(e => e);
await page.goto(server.PREFIX + '/screenshots/translateZ.html'); expect(error.message).toContain('path: unsupported mime type "text/plain"');
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"');
}); });

View file

@ -20,14 +20,17 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
// Printing to pdf is currently only supported in headless chromium. it('should be able to save file', test => {
it.skip(!(options.HEADLESS && options.CHROMIUM))('should be able to save file', async ({page, tmpDir}) => { 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'); const outputFile = path.join(tmpDir, 'output.pdf');
await page.pdf({path: outputFile}); await page.pdf({path: outputFile});
expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0); expect(fs.readFileSync(outputFile).byteLength).toBeGreaterThan(0);
fs.unlinkSync(outputFile); 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); expect(page.pdf).toBe(undefined);
}); });

View file

@ -98,7 +98,11 @@ describe.skip(options.WEBKIT)('permissions', () => {
expect(await getPermission(page, 'geolocation')).toBe('prompt'); 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 // TODO: flaky
// - Linux: https://github.com/microsoft/playwright/pull/1790/checks?check_run_id=587327883 // - Linux: https://github.com/microsoft/playwright/pull/1790/checks?check_run_id=587327883
// - Win: https://ci.appveyor.com/project/aslushnikov/playwright/builds/32402536 // - Win: https://ci.appveyor.com/project/aslushnikov/playwright/builds/32402536
@ -140,8 +144,11 @@ describe.skip(options.WEBKIT)('permissions', () => {
await otherContext.close(); await otherContext.close();
}); });
it.fail(options.WEBKIT || options.FIREFOX || (options.CHROMIUM && !options.HEADLESS))('should support clipboard read', async ({page, server, context, browser}) => { it('should support clipboard read', test => {
// No such permissions (requires flag) in Firefox 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); await page.goto(server.EMPTY_PAGE);
expect(await getPermission(page, 'clipboard-read')).toBe('prompt'); expect(await getPermission(page, 'clipboard-read')).toBe('prompt');
let error; let error;

View file

@ -63,8 +63,9 @@ it('should authenticate', async ({browserType, defaultBrowserOptions, server}) =
await browser.close(); await browser.close();
}); });
it.fail(options.CHROMIUM && !options.HEADLESS)('should exclude patterns', async ({browserType, defaultBrowserOptions, server}) => { it('should exclude patterns', test => {
// Chromium headful crashes with CHECK(!in_frame_tree_) in RenderFrameImpl::OnDeleteFrame. 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) => { server.setRoute('/target.html', async (req, res) => {
res.end('<html><title>Served by the proxy</title></html>'); res.end('<html><title>Served by the proxy</title></html>');
}); });

View file

@ -50,8 +50,9 @@ it('should work with status code 422', async ({page, server}) => {
expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); 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}) => { it('should allow mocking binary responses', test => {
// Firefox headful produces a different image. test.skip(options.FIREFOX && !options.HEADLESS, '// Firefox headful produces a different image.');
}, async ({page, server, golden}) => {
await page.route('**/*', route => { await page.route('**/*', route => {
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png')); const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
route.fulfill({ 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')); 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. // Firefox headful produces a different image.
await page.route('**/*', route => { await page.route('**/*', route => {
route.fulfill({ route.fulfill({

View file

@ -172,132 +172,132 @@ class VideoPlayer {
} }
} }
it.fixme(options.CHROMIUM).flaky(options.FIREFOX && MAC)('should capture static page', async ({page, tmpDir, videoPlayer, toImpl}) => { describe.skip(options.WIRE).fixme(options.CHROMIUM)('screencast', () => {
if (!toImpl) it('should capture static page', test => {
return; test.fixme();
const videoFile = path.join(tmpDir, 'v.webm'); }, async ({page, tmpDir, videoPlayer, toImpl}) => {
await page.evaluate(() => document.body.style.backgroundColor = 'red'); const videoFile = path.join(tmpDir, 'v.webm');
await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480}); await page.evaluate(() => document.body.style.backgroundColor = 'red');
// TODO: in WebKit figure out why video size is not reported correctly for await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480});
// static pictures. // TODO: in WebKit figure out why video size is not reported correctly for
if (options.HEADLESS && options.WEBKIT) // static pictures.
await page.setViewportSize({width: 1270, height: 950}); if (options.HEADLESS && options.WEBKIT)
await new Promise(r => setTimeout(r, 300)); await page.setViewportSize({width: 1270, height: 950});
await toImpl(page)._delegate.stopScreencast(); await new Promise(r => setTimeout(r, 300));
expect(fs.existsSync(videoFile)).toBe(true); await toImpl(page)._delegate.stopScreencast();
expect(fs.existsSync(videoFile)).toBe(true);
await videoPlayer.load(videoFile); await videoPlayer.load(videoFile);
const duration = await videoPlayer.duration(); const duration = await videoPlayer.duration();
expect(duration).toBeGreaterThan(0); expect(duration).toBeGreaterThan(0);
expect(await videoPlayer.videoWidth()).toBe(640); expect(await videoPlayer.videoWidth()).toBe(640);
expect(await videoPlayer.videoHeight()).toBe(480); 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(); await videoPlayer.seekLastNonEmptyFrame();
const pixels = await videoPlayer.pixels(); 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); expectAll(pixels, almostRed);
} });
});
it('should capture navigation', test => {
it.slow().fixme(options.CHROMIUM)('should fire start/stop events when page created/closed', async ({browser, tmpDir, server, toImpl}) => { test.flaky(options.WEBKIT);
if (!toImpl) }, async ({page, tmpDir, server, videoPlayer, toImpl}) => {
return; const videoFile = path.join(tmpDir, 'v.webm');
// Use server side of the context. All the code below also uses server side APIs. await page.goto(server.PREFIX + '/background-color.html#rgb(0,0,0)');
const context = toImpl(await browser.newContext()); await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480});
await context._enableScreencast({width: 640, height: 480, dir: tmpDir}); // TODO: in WebKit figure out why video size is not reported correctly for
expect(context._screencastOptions).toBeTruthy(); // static pictures.
if (options.HEADLESS && options.WEBKIT)
const [screencast, newPage] = await Promise.all([ await page.setViewportSize({width: 1270, height: 950});
new Promise(resolve => context.on('screencaststarted', resolve)) as Promise<any>, await new Promise(r => setTimeout(r, 300));
context.newPage(), await page.goto(server.CROSS_PROCESS_PREFIX + '/background-color.html#rgb(100,100,100)');
]); await new Promise(r => setTimeout(r, 300));
expect(screencast.page === newPage).toBe(true); await toImpl(page)._delegate.stopScreencast();
expect(fs.existsSync(videoFile)).toBe(true);
const [videoFile] = await Promise.all([
screencast.path(), await videoPlayer.load(videoFile);
newPage.close(), const duration = await videoPlayer.duration();
]); expect(duration).toBeGreaterThan(0);
expect(path.dirname(videoFile)).toBe(tmpDir);
await context.close(); {
}); await videoPlayer.seekFirstNonEmptyFrame();
const pixels = await videoPlayer.pixels();
it.fixme(options.CHROMIUM)('should fire start event for popups', async ({browser, tmpDir, server, toImpl}) => { expectAll(pixels, almostBlack);
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 videoPlayer.seekLastNonEmptyFrame();
await context._enableScreencast({width: 640, height: 480, dir: tmpDir}); const pixels = await videoPlayer.pixels();
expect(context._screencastOptions).toBeTruthy(); expectAll(pixels, almostGrey);
}
const page = await context.newPage(); });
await page.mainFrame().goto(server.EMPTY_PAGE);
const [screencast, popup] = await Promise.all([ it('should capture css transformation', test => {
new Promise(resolve => context.on('screencaststarted', resolve)) as Promise<any>, test.fixme(options.WEBKIT && WIN, 'Accelerated compositing is disabled in WebKit on Windows.');
new Promise(resolve => context.on('page', resolve)) as Promise<any>, test.flaky(options.WEBKIT && LINUX);
page.mainFrame()._evaluateExpression(() => { }, async ({page, tmpDir, server, videoPlayer, toImpl}) => {
const win = window.open('about:blank'); const videoFile = path.join(tmpDir, 'v.webm');
win.close(); await page.goto(server.PREFIX + '/rotate-z.html');
}, true) await toImpl(page)._delegate.startScreencast({outputFile: videoFile, width: 640, height: 480});
]); // TODO: in WebKit figure out why video size is not reported correctly for
expect(screencast.page === popup).toBe(true); // static pictures.
expect(path.dirname(await screencast.path())).toBe(tmpDir); if (options.HEADLESS && options.WEBKIT)
await context.close(); 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<any>,
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<any>,
new Promise(resolve => context.on('page', resolve)) as Promise<any>,
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();
});
}); });

View file

@ -44,69 +44,172 @@ async function checkPageSlowMo(toImpl, page, task) {
`); `);
await checkSlowMo(toImpl, page, task); await checkSlowMo(toImpl, page, task);
} }
describe.skip(options.WIRE)('slowMo', () => {
it.skip(options.WIRE)('Page SlowMo $$eval', async ({page, toImpl}) => { it('Page SlowMo $$eval', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.$$eval('button', () => void 0)); await checkPageSlowMo(toImpl, page, () => page.$$eval('button', () => void 0));
}); });
it.skip(options.WIRE)('Page SlowMo $eval', async ({page, toImpl}) => { it('Page SlowMo $eval', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.$eval('button', () => void 0)); await checkPageSlowMo(toImpl, page, () => page.$eval('button', () => void 0));
}); });
it.skip(options.WIRE)('Page SlowMo check', async ({page, toImpl}) => { it('Page SlowMo check', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.check('.check')); await checkPageSlowMo(toImpl, page, () => page.check('.check'));
}); });
it.skip(options.WIRE)('Page SlowMo click', async ({page, toImpl}) => { it('Page SlowMo click', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.click('button')); await checkPageSlowMo(toImpl, page, () => page.click('button'));
}); });
it.skip(options.WIRE)('Page SlowMo dblclick', async ({page, toImpl}) => { it('Page SlowMo dblclick', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.dblclick('button')); await checkPageSlowMo(toImpl, page, () => page.dblclick('button'));
}); });
it.skip(options.WIRE)('Page SlowMo dispatchEvent', async ({page, toImpl}) => { it('Page SlowMo dispatchEvent', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.dispatchEvent('button', 'click')); await checkPageSlowMo(toImpl, page, () => page.dispatchEvent('button', 'click'));
}); });
it.skip(options.WIRE)('Page SlowMo emulateMedia', async ({page, toImpl}) => { it('Page SlowMo emulateMedia', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.emulateMedia({media: 'print'})); await checkPageSlowMo(toImpl, page, () => page.emulateMedia({media: 'print'}));
}); });
it.skip(options.WIRE)('Page SlowMo evaluate', async ({page, toImpl}) => { it('Page SlowMo evaluate', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.evaluate(() => void 0)); await checkPageSlowMo(toImpl, page, () => page.evaluate(() => void 0));
}); });
it.skip(options.WIRE)('Page SlowMo evaluateHandle', async ({page, toImpl}) => { it('Page SlowMo evaluateHandle', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.evaluateHandle(() => window)); await checkPageSlowMo(toImpl, page, () => page.evaluateHandle(() => window));
}); });
it.skip(options.WIRE)('Page SlowMo fill', async ({page, toImpl}) => { it('Page SlowMo fill', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.fill('.fill', 'foo')); await checkPageSlowMo(toImpl, page, () => page.fill('.fill', 'foo'));
}); });
it.skip(options.WIRE)('Page SlowMo focus', async ({page, toImpl}) => { it('Page SlowMo focus', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.focus('button')); await checkPageSlowMo(toImpl, page, () => page.focus('button'));
}); });
it.skip(options.WIRE)('Page SlowMo goto', async ({page, toImpl}) => { it('Page SlowMo goto', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.goto('about:blank')); await checkPageSlowMo(toImpl, page, () => page.goto('about:blank'));
}); });
it.skip(options.WIRE)('Page SlowMo hover', async ({page, toImpl}) => { it('Page SlowMo hover', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.hover('button')); await checkPageSlowMo(toImpl, page, () => page.hover('button'));
}); });
it.skip(options.WIRE)('Page SlowMo press', async ({page, toImpl}) => { it('Page SlowMo press', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.press('button', 'Enter')); await checkPageSlowMo(toImpl, page, () => page.press('button', 'Enter'));
}); });
it.skip(options.WIRE)('Page SlowMo reload', async ({page, toImpl}) => { it('Page SlowMo reload', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.reload()); await checkPageSlowMo(toImpl, page, () => page.reload());
}); });
it.skip(options.WIRE)('Page SlowMo setContent', async ({page, toImpl}) => { it('Page SlowMo setContent', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.setContent('hello world')); await checkPageSlowMo(toImpl, page, () => page.setContent('hello world'));
}); });
it.skip(options.WIRE)('Page SlowMo selectOption', async ({page, toImpl}) => { it('Page SlowMo selectOption', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.selectOption('select', 'foo')); await checkPageSlowMo(toImpl, page, () => page.selectOption('select', 'foo'));
}); });
it.skip(options.WIRE)('Page SlowMo setInputFiles', async ({page, toImpl}) => { it('Page SlowMo setInputFiles', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.setInputFiles('.file', [])); await checkPageSlowMo(toImpl, page, () => page.setInputFiles('.file', []));
}); });
it.skip(options.WIRE)('Page SlowMo setViewportSize', async ({page, toImpl}) => { it('Page SlowMo setViewportSize', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.setViewportSize({height: 400, width: 400})); await checkPageSlowMo(toImpl, page, () => page.setViewportSize({height: 400, width: 400}));
}); });
it.skip(options.WIRE)('Page SlowMo type', async ({page, toImpl}) => { it('Page SlowMo type', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.type('.fill', 'a')); await checkPageSlowMo(toImpl, page, () => page.type('.fill', 'a'));
}); });
it.skip(options.WIRE)('Page SlowMo uncheck', async ({page, toImpl}) => { it('Page SlowMo uncheck', async ({page, toImpl}) => {
await checkPageSlowMo(toImpl, page, () => page.uncheck('.uncheck')); 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) { 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)); 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) { async function checkElementSlowMo(toImpl, page, selector, task) {
await page.setContent(` await page.setContent(`
@ -193,51 +242,3 @@ async function checkElementSlowMo(toImpl, page, selector, task) {
const element = await page.$(selector); const element = await page.$(selector);
await checkSlowMo(toImpl, page, task.bind(null, element)); 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());
});

View file

@ -16,7 +16,9 @@
import { options } from './playwright.fixtures'; 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 page.goto(server.PREFIX + '/snapshot/snapshot-with-css.html');
await (context as any).__snapshotter.captureSnapshot(toImpl(page), { timeout: 5000, label: 'snapshot' }); await (context as any).__snapshotter.captureSnapshot(toImpl(page), { timeout: 5000, label: 'snapshot' });
}); });