test: remove describes (3) (#3278)

This commit is contained in:
Pavel Feldman 2020-08-03 16:30:37 -07:00 committed by GitHub
parent de55fa6482
commit 573f580fa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 2386 additions and 2137 deletions

View file

@ -0,0 +1,348 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, HEADLESS} = testOptions;
const {PNG} = require('pngjs');
// Firefox headful produces a different image.
const ffheadful = FFOX && !HEADLESS;
it.skip(ffheadful)('should work', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(50, 100));
const elementHandle = await page.$('.box:nth-of-type(3)');
const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
});
it.skip(ffheadful)('should take into account padding and border', async({page}) => {
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).toBeGolden('screenshot-element-padding-border.png');
});
it.skip(ffheadful)('should capture full element when larger than viewport in parallel', async({page}) => {
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]).toBeGolden('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}) => {
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).toBeGolden('screenshot-element-larger-than-viewport.png');
await utils.verifyViewport(page, 500, 500);
});
it.skip(ffheadful)('should scroll element into view', async({page}) => {
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).toBeGolden('screenshot-element-scrolled-into-view.png');
});
it.skip(ffheadful)('should scroll 15000px into view', async({page}) => {
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).toBeGolden('screenshot-element-scrolled-into-view.png');
});
it.skip(ffheadful)('should work with a rotated element', async({page}) => {
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).toBeGolden('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}) => {
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).toBeGolden('screenshot-element-bounding-box.png');
});
it.skip(ffheadful)('should work for an element with fractional dimensions', async({page}) => {
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).toBeGolden('screenshot-element-fractional.png');
});
it.skip(FFOX)('should work with a mobile viewport', 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 + '/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).toBeGolden('screenshot-element-mobile.png');
await context.close();
});
it.skip(FFOX)('should work with device scale factor', async({browser, server}) => {
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).toBeGolden('screenshot-element-mobile-dsf.png');
await context.close();
});
it.skip(ffheadful)('should work for an element with an offset', async({page}) => {
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).toBeGolden('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 }));
expect(sizeBefore.width).toBe(sizeAfter.width);
expect(sizeBefore.height).toBe(sizeAfter.height);
await context.close();
});
it.skip(ffheadful)('should restore default viewport after fullPage screenshot', async({ browser }) => {
const context = await browser.newContext({ viewport: { width: 456, height: 789 } });
const page = await context.newPage();
await utils.verifyViewport(page, 456, 789);
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeInstanceOf(Buffer);
await utils.verifyViewport(page, 456, 789);
await context.close();
});
it.skip(ffheadful || USES_HOOKS)('should restore viewport after page screenshot and exception', async({ browser, server }) => {
const context = await browser.newContext({ viewport: { width: 350, height: 360 } });
const page = await context.newPage();
await page.goto(server.PREFIX + '/grid.html');
const __testHookBeforeScreenshot = () => { throw new Error('oh my') };
const error = await page.screenshot({ fullPage: true, __testHookBeforeScreenshot }).catch(e => e);
expect(error.message).toContain('oh my');
await utils.verifyViewport(page, 350, 360);
await context.close();
});
it.skip(ffheadful || USES_HOOKS)('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 }).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({ viewport: null });
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 || USES_HOOKS)('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 }).catch(e => e);
expect(error.message).toContain('oh my');
await utils.verifyViewport(page, 350, 360);
await context.close();
});
it.skip(ffheadful)('should wait for element to stop moving', async({page, server}) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
const elementHandle = await page.$('.box:nth-of-type(3)');
await elementHandle.evaluate(e => {
e.classList.add('animation');
return new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)));
});
const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
});
it.skip(ffheadful)('should take screenshot of disabled button', async({page}) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.setContent(`<button disabled>Click me</button>`);
const button = await page.$('button');
const screenshot = await button.screenshot();
expect(screenshot).toBeInstanceOf(Buffer);
});

View file

@ -0,0 +1,46 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => document.body);
const element = aHandle.asElement();
expect(element).toBeTruthy();
});
it('should return null for non-elements', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => 2);
const element = aHandle.asElement();
expect(element).toBeFalsy();
});
it('should return ElementHandle for TextNodes', async({page, server}) => {
await page.setContent('<div>ee!</div>');
const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
const element = aHandle.asElement();
expect(element).toBeTruthy();
expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element)).toBeTruthy();
});
it('should work with nullified Node', async({page, server}) => {
await page.setContent('<section>test</section>');
await page.evaluate('delete Node');
const handle = await page.evaluateHandle(() => document.querySelector('section'));
const element = handle.asElement();
expect(element).not.toBe(null);
});

View file

@ -0,0 +1,37 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
const json = await aHandle.jsonValue();
expect(json).toEqual({foo: 'bar'});
});
it('should work with dates', async({page, server}) => {
const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
const date = await dateHandle.jsonValue();
expect(date.toJSON()).toBe('2017-09-26T00:00:00.000Z');
});
it('should throw for circular objects', async({page, server}) => {
const windowHandle = await page.evaluateHandle('window');
let error = null;
await windowHandle.jsonValue().catch(e => error = e);
expect(error.message).toContain('Argument is a circular structure');
});

View file

@ -0,0 +1,94 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
one: 1,
two: 2,
three: 3
}));
const twoHandle = await aHandle.getProperty('two');
expect(await twoHandle.jsonValue()).toEqual(2);
});
it('should work with undefined, null, and empty', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
undefined: undefined,
null: null,
}));
const undefinedHandle = await aHandle.getProperty('undefined');
expect(String(await undefinedHandle.jsonValue())).toEqual('undefined');
const nullHandle = await aHandle.getProperty('null');
expect(await nullHandle.jsonValue()).toEqual(null);
const emptyhandle = await aHandle.getProperty('empty');
expect(String(await emptyhandle.jsonValue())).toEqual('undefined');
});
it('should work with unserializable values', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
infinity: Infinity,
nInfinity: -Infinity,
nan: NaN,
nzero: -0
}));
const infinityHandle = await aHandle.getProperty('infinity');
expect(await infinityHandle.jsonValue()).toEqual(Infinity);
const nInfinityHandle = await aHandle.getProperty('nInfinity');
expect(await nInfinityHandle.jsonValue()).toEqual(-Infinity);
const nanHandle = await aHandle.getProperty('nan');
expect(String(await nanHandle.jsonValue())).toEqual('NaN');
const nzeroHandle = await aHandle.getProperty('nzero');
expect(await nzeroHandle.jsonValue()).toEqual(-0);
});
it('getProperties should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
foo: 'bar'
}));
const properties = await aHandle.getProperties();
const foo = properties.get('foo');
expect(foo).toBeTruthy();
expect(await foo.jsonValue()).toBe('bar');
});
it('getProperties should return empty map for non-objects', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => 123);
const properties = await aHandle.getProperties();
expect(properties.size).toBe(0);
});
it('getProperties should return even non-own properties', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => {
class A {
constructor() {
this.a = '1';
}
}
class B extends A {
constructor() {
super();
this.b = '2';
}
}
return new B();
});
const properties = await aHandle.getProperties();
expect(await properties.get('a').jsonValue()).toBe('1');
expect(await properties.get('b').jsonValue()).toBe('2');
});

View file

@ -0,0 +1,59 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('should work for primitives', async({page, server}) => {
const numberHandle = await page.evaluateHandle(() => 2);
expect(numberHandle.toString()).toBe('JSHandle@2');
const stringHandle = await page.evaluateHandle(() => 'a');
expect(stringHandle.toString()).toBe('JSHandle@a');
});
it('should work for complicated objects', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => window);
expect(aHandle.toString()).toBe('JSHandle@object');
});
it('should work for promises', async({page, server}) => {
// wrap the promise in an object, otherwise we will await.
const wrapperHandle = await page.evaluateHandle(() => ({b: Promise.resolve(123)}));
const bHandle = await wrapperHandle.getProperty('b');
expect(bHandle.toString()).toBe('JSHandle@promise');
});
it('should work with different subtypes', async({page, server}) => {
expect((await page.evaluateHandle('(function(){})')).toString()).toBe('JSHandle@function');
expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle@12');
expect((await page.evaluateHandle('true')).toString()).toBe('JSHandle@true');
expect((await page.evaluateHandle('undefined')).toString()).toBe('JSHandle@undefined');
expect((await page.evaluateHandle('"foo"')).toString()).toBe('JSHandle@foo');
expect((await page.evaluateHandle('Symbol()')).toString()).toBe('JSHandle@symbol');
expect((await page.evaluateHandle('new Map()')).toString()).toBe('JSHandle@map');
expect((await page.evaluateHandle('new Set()')).toString()).toBe('JSHandle@set');
expect((await page.evaluateHandle('[]')).toString()).toBe('JSHandle@array');
expect((await page.evaluateHandle('null')).toString()).toBe('JSHandle@null');
expect((await page.evaluateHandle('/foo/')).toString()).toBe('JSHandle@regexp');
expect((await page.evaluateHandle('document.body')).toString()).toBe('JSHandle@node');
expect((await page.evaluateHandle('new Date()')).toString()).toBe('JSHandle@date');
expect((await page.evaluateHandle('new WeakMap()')).toString()).toBe('JSHandle@weakmap');
expect((await page.evaluateHandle('new WeakSet()')).toString()).toBe('JSHandle@weakset');
expect((await page.evaluateHandle('new Error()')).toString()).toBe('JSHandle@error');
// TODO(yurys): change subtype from array to typedarray in WebKit.
expect((await page.evaluateHandle('new Int32Array()')).toString()).toBe(WEBKIT ? 'JSHandle@array' : 'JSHandle@typedarray');
expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy');
});

View file

@ -1,279 +0,0 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
describe('Page.evaluateHandle', function() {
it('should work', async({page, server}) => {
const windowHandle = await page.evaluateHandle(() => window);
expect(windowHandle).toBeTruthy();
});
it('should accept object handle as an argument', async({page, server}) => {
const navigatorHandle = await page.evaluateHandle(() => navigator);
const text = await page.evaluate(e => e.userAgent, navigatorHandle);
expect(text).toContain('Mozilla');
});
it('should accept object handle to primitive types', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => 5);
const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
expect(isFive).toBeTruthy();
});
it('should accept nested handle', async({page, server}) => {
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
const result = await page.evaluate(({ foo }) => {
return foo;
}, { foo });
expect(result).toEqual({ x: 1, y: 'foo' });
});
it('should accept nested window handle', async({page, server}) => {
const foo = await page.evaluateHandle(() => window);
const result = await page.evaluate(({ foo }) => {
return foo === window;
}, { foo });
expect(result).toBe(true);
});
it('should accept multiple nested handles', async({page, server}) => {
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
const bar = await page.evaluateHandle(() => 5);
const baz = await page.evaluateHandle(() => (['baz']));
const result = await page.evaluate(x => {
return JSON.stringify(x);
}, { a1: { foo }, a2: { bar, arr: [{ baz }] } });
expect(JSON.parse(result)).toEqual({
a1: { foo: { x: 1, y: 'foo' } },
a2: { bar: 5, arr: [{ baz: ['baz'] }] }
});
});
it('should throw for circular objects', async({page, server}) => {
const a = { x: 1 };
a.y = a;
const error = await page.evaluate(x => x, a).catch(e => e);
expect(error.message).toContain('Argument is a circular structure');
});
it('should accept same handle multiple times', async({page, server}) => {
const foo = await page.evaluateHandle(() => 1);
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: 1, bar: [1], baz: { foo: 1 } });
});
it('should accept same nested object multiple times', async({page, server}) => {
const foo = { x: 1 };
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: { x: 1 }, bar: [{ x : 1 }], baz: { foo: { x : 1 } } });
});
it('should accept object handle to unserializable value', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => Infinity);
expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true);
});
it('should pass configurable args', async({page, server}) => {
const result = await page.evaluate(arg => {
if (arg.foo !== 42)
throw new Error('Not a 42');
arg.foo = 17;
if (arg.foo !== 17)
throw new Error('Not 17');
delete arg.foo;
if (arg.foo === 17)
throw new Error('Still 17');
return arg;
}, { foo: 42 });
expect(result).toEqual({});
});
it('should work with primitives', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => {
window.FOO = 123;
return window;
});
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123);
});
});
describe('JSHandle.evaluate', function() {
it('should work with function', async({page, server}) => {
const windowHandle = await page.evaluateHandle(() => {
window.foo = [1, 2];
return window;
});
expect(await windowHandle.evaluate(w => w.foo)).toEqual([1, 2]);
});
it('should work with expression', async({page, server}) => {
const windowHandle = await page.evaluateHandle(() => {
window.foo = [1, 2];
return window;
});
expect(await windowHandle.evaluate('window.foo')).toEqual([1, 2]);
});
});
describe('JSHandle.getProperty', function() {
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
one: 1,
two: 2,
three: 3
}));
const twoHandle = await aHandle.getProperty('two');
expect(await twoHandle.jsonValue()).toEqual(2);
});
it('should work with undefined, null, and empty', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
undefined: undefined,
null: null,
}));
const undefinedHandle = await aHandle.getProperty('undefined');
expect(String(await undefinedHandle.jsonValue())).toEqual('undefined');
const nullHandle = await aHandle.getProperty('null');
expect(await nullHandle.jsonValue()).toEqual(null);
const emptyhandle = await aHandle.getProperty('empty');
expect(String(await emptyhandle.jsonValue())).toEqual('undefined');
});
it('should work with unserializable values', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
infinity: Infinity,
nInfinity: -Infinity,
nan: NaN,
nzero: -0
}));
const infinityHandle = await aHandle.getProperty('infinity');
expect(await infinityHandle.jsonValue()).toEqual(Infinity);
const nInfinityHandle = await aHandle.getProperty('nInfinity');
expect(await nInfinityHandle.jsonValue()).toEqual(-Infinity);
const nanHandle = await aHandle.getProperty('nan');
expect(String(await nanHandle.jsonValue())).toEqual('NaN');
const nzeroHandle = await aHandle.getProperty('nzero');
expect(await nzeroHandle.jsonValue()).toEqual(-0);
});
});
describe('JSHandle.jsonValue', function() {
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({foo: 'bar'}));
const json = await aHandle.jsonValue();
expect(json).toEqual({foo: 'bar'});
});
it('should work with dates', async({page, server}) => {
const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z'));
const date = await dateHandle.jsonValue();
expect(date.toJSON()).toBe('2017-09-26T00:00:00.000Z');
});
it('should throw for circular objects', async({page, server}) => {
const windowHandle = await page.evaluateHandle('window');
let error = null;
await windowHandle.jsonValue().catch(e => error = e);
expect(error.message).toContain('Argument is a circular structure');
});
});
describe('JSHandle.getProperties', function() {
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => ({
foo: 'bar'
}));
const properties = await aHandle.getProperties();
const foo = properties.get('foo');
expect(foo).toBeTruthy();
expect(await foo.jsonValue()).toBe('bar');
});
it('should return empty map for non-objects', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => 123);
const properties = await aHandle.getProperties();
expect(properties.size).toBe(0);
});
it('should return even non-own properties', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => {
class A {
constructor() {
this.a = '1';
}
}
class B extends A {
constructor() {
super();
this.b = '2';
}
}
return new B();
});
const properties = await aHandle.getProperties();
expect(await properties.get('a').jsonValue()).toBe('1');
expect(await properties.get('b').jsonValue()).toBe('2');
});
});
describe('JSHandle.asElement', function() {
it('should work', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => document.body);
const element = aHandle.asElement();
expect(element).toBeTruthy();
});
it('should return null for non-elements', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => 2);
const element = aHandle.asElement();
expect(element).toBeFalsy();
});
it('should return ElementHandle for TextNodes', async({page, server}) => {
await page.setContent('<div>ee!</div>');
const aHandle = await page.evaluateHandle(() => document.querySelector('div').firstChild);
const element = aHandle.asElement();
expect(element).toBeTruthy();
expect(await page.evaluate(e => e.nodeType === HTMLElement.TEXT_NODE, element)).toBeTruthy();
});
it('should work with nullified Node', async({page, server}) => {
await page.setContent('<section>test</section>');
await page.evaluate('delete Node');
const handle = await page.evaluateHandle(() => document.querySelector('section'));
const element = handle.asElement();
expect(element).not.toBe(null);
});
});
describe('JSHandle.toString', function() {
it('should work for primitives', async({page, server}) => {
const numberHandle = await page.evaluateHandle(() => 2);
expect(numberHandle.toString()).toBe('JSHandle@2');
const stringHandle = await page.evaluateHandle(() => 'a');
expect(stringHandle.toString()).toBe('JSHandle@a');
});
it('should work for complicated objects', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => window);
expect(aHandle.toString()).toBe('JSHandle@object');
});
it('should work for promises', async({page, server}) => {
// wrap the promise in an object, otherwise we will await.
const wrapperHandle = await page.evaluateHandle(() => ({b: Promise.resolve(123)}));
const bHandle = await wrapperHandle.getProperty('b');
expect(bHandle.toString()).toBe('JSHandle@promise');
});
it('should work with different subtypes', async({page, server}) => {
expect((await page.evaluateHandle('(function(){})')).toString()).toBe('JSHandle@function');
expect((await page.evaluateHandle('12')).toString()).toBe('JSHandle@12');
expect((await page.evaluateHandle('true')).toString()).toBe('JSHandle@true');
expect((await page.evaluateHandle('undefined')).toString()).toBe('JSHandle@undefined');
expect((await page.evaluateHandle('"foo"')).toString()).toBe('JSHandle@foo');
expect((await page.evaluateHandle('Symbol()')).toString()).toBe('JSHandle@symbol');
expect((await page.evaluateHandle('new Map()')).toString()).toBe('JSHandle@map');
expect((await page.evaluateHandle('new Set()')).toString()).toBe('JSHandle@set');
expect((await page.evaluateHandle('[]')).toString()).toBe('JSHandle@array');
expect((await page.evaluateHandle('null')).toString()).toBe('JSHandle@null');
expect((await page.evaluateHandle('/foo/')).toString()).toBe('JSHandle@regexp');
expect((await page.evaluateHandle('document.body')).toString()).toBe('JSHandle@node');
expect((await page.evaluateHandle('new Date()')).toString()).toBe('JSHandle@date');
expect((await page.evaluateHandle('new WeakMap()')).toString()).toBe('JSHandle@weakmap');
expect((await page.evaluateHandle('new WeakSet()')).toString()).toBe('JSHandle@weakset');
expect((await page.evaluateHandle('new Error()')).toString()).toBe('JSHandle@error');
// TODO(yurys): change subtype from array to typedarray in WebKit.
expect((await page.evaluateHandle('new Int32Array()')).toString()).toBe(WEBKIT ? 'JSHandle@array' : 'JSHandle@typedarray');
expect((await page.evaluateHandle('new Proxy({}, {})')).toString()).toBe('JSHandle@proxy');
});
});

View file

@ -1,109 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
describe('BrowserContext', function() {
it('should work across sessions', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
expect(browser1.contexts().length).toBe(0);
await browser1.newContext();
expect(browser1.contexts().length).toBe(1);
const browser2 = await browserType.connect({ wsEndpoint: browserServer.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();
await browser2.close();
await browserServer._checkLeaks();
await browserServer.close();
});
});
describe('Browser.Events.disconnected', function() {
it.slow()('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const originalBrowser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const wsEndpoint = browserServer.wsEndpoint();
const remoteBrowser1 = await browserType.connect({ wsEndpoint });
const remoteBrowser2 = await browserType.connect({ wsEndpoint });
let disconnectedOriginal = 0;
let disconnectedRemote1 = 0;
let disconnectedRemote2 = 0;
originalBrowser.on('disconnected', () => ++disconnectedOriginal);
remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);
await Promise.all([
new Promise(f => remoteBrowser2.on('disconnected', f)),
remoteBrowser2.close(),
]);
expect(disconnectedOriginal).toBe(0);
expect(disconnectedRemote1).toBe(0);
expect(disconnectedRemote2).toBe(1);
await Promise.all([
new Promise(f => remoteBrowser1.on('disconnected', f)),
new Promise(f => originalBrowser.on('disconnected', f)),
browserServer.close(),
]);
expect(disconnectedOriginal).toBe(1);
expect(disconnectedRemote1).toBe(1);
expect(disconnectedRemote2).toBe(1);
});
});
describe('browserType.connect', function() {
it('should be able to connect multiple times to the same browser', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page1 = await browser1.newPage();
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
browser1.close();
const page2 = await browser2.newPage();
expect(await page2.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
await browser2.close();
await browserServer._checkLeaks();
await browserServer.close();
});
it('should not be able to close remote browser', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
{
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
await remote.newContext();
await remote.close();
}
{
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
await remote.newContext();
await remote.close();
}
await browserServer._checkLeaks();
await browserServer.close();
});
});

104
test/multiclient.spec.js Normal file
View file

@ -0,0 +1,104 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('should work across sessions', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
expect(browser1.contexts().length).toBe(0);
await browser1.newContext();
expect(browser1.contexts().length).toBe(1);
const browser2 = await browserType.connect({ wsEndpoint: browserServer.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();
await browser2.close();
await browserServer._checkLeaks();
await browserServer.close();
});
it.slow()('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async ({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const originalBrowser = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const wsEndpoint = browserServer.wsEndpoint();
const remoteBrowser1 = await browserType.connect({ wsEndpoint });
const remoteBrowser2 = await browserType.connect({ wsEndpoint });
let disconnectedOriginal = 0;
let disconnectedRemote1 = 0;
let disconnectedRemote2 = 0;
originalBrowser.on('disconnected', () => ++disconnectedOriginal);
remoteBrowser1.on('disconnected', () => ++disconnectedRemote1);
remoteBrowser2.on('disconnected', () => ++disconnectedRemote2);
await Promise.all([
new Promise(f => remoteBrowser2.on('disconnected', f)),
remoteBrowser2.close(),
]);
expect(disconnectedOriginal).toBe(0);
expect(disconnectedRemote1).toBe(0);
expect(disconnectedRemote2).toBe(1);
await Promise.all([
new Promise(f => remoteBrowser1.on('disconnected', f)),
new Promise(f => originalBrowser.on('disconnected', f)),
browserServer.close(),
]);
expect(disconnectedOriginal).toBe(1);
expect(disconnectedRemote1).toBe(1);
expect(disconnectedRemote2).toBe(1);
});
it('should be able to connect multiple times to the same browser', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
const browser1 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const browser2 = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
const page1 = await browser1.newPage();
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
browser1.close();
const page2 = await browser2.newPage();
expect(await page2.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
await browser2.close();
await browserServer._checkLeaks();
await browserServer.close();
});
it('should not be able to close remote browser', async({browserType, defaultBrowserOptions}) => {
const browserServer = await browserType.launchServer(defaultBrowserOptions);
{
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
await remote.newContext();
await remote.close();
}
{
const remote = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() });
await remote.newContext();
await remote.close();
}
await browserServer._checkLeaks();
await browserServer.close();
});

View file

@ -0,0 +1,192 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
it('should work for main frame navigation request', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame());
});
it('should work for subframe navigation request', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const requests = [];
page.on('request', request => requests.push(request));
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.frames()[1]);
});
it('should work for fetch requests', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
let requests = [];
page.on('request', request => requests.push(request));
await page.evaluate(() => fetch('/digits/1.png'));
requests = requests.filter(request => !request.url().includes('favicon'));
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame());
});
it('should return headers', async({page, server}) => {
const response = await page.goto(server.EMPTY_PAGE);
if (CHROMIUM)
expect(response.request().headers()['user-agent']).toContain('Chrome');
else if (FFOX)
expect(response.request().headers()['user-agent']).toContain('Firefox');
else if (WEBKIT)
expect(response.request().headers()['user-agent']).toContain('WebKit');
});
it.fail(CHROMIUM||WEBKIT)('should get the same headers as the server', async({page, server}) => {
await page.goto(server.PREFIX + '/empty.html');
let serverRequest;
await server.setRoute('/something', (request, response) => {
serverRequest = request;
response.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
response.end('done');
});
const requestPromise = page.waitForEvent('request');
const text = await page.evaluate(async url => {
const data = await fetch(url);
return data.text();
}, server.CROSS_PROCESS_PREFIX + '/something');
const request = await requestPromise;
expect(text).toBe('done');
expect(request.headers()).toEqual(serverRequest.headers);
});
it('should return postData', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
expect(request).toBeTruthy();
expect(request.postData()).toBe('{"foo":"bar"}');
});
it('should work with binary post data', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(async () => {
await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) })
});
expect(request).toBeTruthy();
const buffer = request.postDataBuffer();
expect(buffer.length).toBe(256);
for (let i = 0; i < 256; ++i)
expect(buffer[i]).toBe(i);
});
it('should work with binary post data and interception', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
await page.route('/post', route => route.continue());
page.on('request', r => request = r);
await page.evaluate(async () => {
await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) })
});
expect(request).toBeTruthy();
const buffer = request.postDataBuffer();
expect(buffer.length).toBe(256);
for (let i = 0; i < 256; ++i)
expect(buffer[i]).toBe(i);
});
it('should be |undefined| when there is no post data', async({page, server}) => {
const response = await page.goto(server.EMPTY_PAGE);
expect(response.request().postData()).toBe(null);
});
it('should parse the json post data', async ({ page, server }) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({ foo: 'bar' }) }));
expect(request).toBeTruthy();
expect(request.postDataJSON()).toEqual({ "foo": "bar" });
});
it('should parse the data if content-type is application/x-www-form-urlencoded', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.setContent(`<form method='POST' action='/post'><input type='text' name='foo' value='bar'><input type='number' name='baz' value='123'><input type='submit'></form>`);
await page.click('input[type=submit]');
expect(request).toBeTruthy();
expect(request.postDataJSON()).toEqual({'foo':'bar','baz':'123'});
})
it('should be |undefined| when there is no post data', async ({ page, server }) => {
const response = await page.goto(server.EMPTY_PAGE);
expect(response.request().postDataJSON()).toBe(null);
});
it('should return event source', async ({page, server}) => {
const SSE_MESSAGE = {foo: 'bar'};
// 1. Setup server-sent events on server that immediately sends a message to the client.
server.setRoute('/sse', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
});
res.write(`data: ${JSON.stringify(SSE_MESSAGE)}\n\n`);
});
// 2. Subscribe to page request events.
await page.goto(server.EMPTY_PAGE);
const requests = [];
page.on('request', request => requests.push(request));
// 3. Connect to EventSource in browser and return first message.
expect(await page.evaluate(() => {
const eventSource = new EventSource('/sse');
return new Promise(resolve => {
eventSource.onmessage = e => resolve(JSON.parse(e.data));
});
})).toEqual(SSE_MESSAGE);
expect(requests[0].resourceType()).toBe('eventsource');
});
it('should return navigation bit', async({page, server}) => {
const requests = new Map();
page.on('request', request => requests.set(request.url().split('/').pop(), request));
server.setRedirect('/rrredirect', '/frames/one-frame.html');
await page.goto(server.PREFIX + '/rrredirect');
expect(requests.get('rrredirect').isNavigationRequest()).toBe(true);
expect(requests.get('one-frame.html').isNavigationRequest()).toBe(true);
expect(requests.get('frame.html').isNavigationRequest()).toBe(true);
expect(requests.get('script.js').isNavigationRequest()).toBe(false);
expect(requests.get('style.css').isNavigationRequest()).toBe(false);
});
it('should return navigation bit when navigating to image', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.PREFIX + '/pptr.png');
expect(requests[0].isNavigationRequest()).toBe(true);
});

View file

@ -0,0 +1,118 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
it('should work', async({page, server}) => {
server.setRoute('/empty.html', (req, res) => {
res.setHeader('foo', 'bar');
res.end();
});
const response = await page.goto(server.EMPTY_PAGE);
expect(response.headers()['foo']).toBe('bar');
});
it('should return text', async({page, server}) => {
const response = await page.goto(server.PREFIX + '/simple.json');
expect(await response.text()).toBe('{"foo": "bar"}\n');
});
it('should return uncompressed text', async({page, server}) => {
server.enableGzip('/simple.json');
const response = await page.goto(server.PREFIX + '/simple.json');
expect(response.headers()['content-encoding']).toBe('gzip');
expect(await response.text()).toBe('{"foo": "bar"}\n');
});
it('should throw when requesting body of redirected response', async({page, server}) => {
server.setRedirect('/foo.html', '/empty.html');
const response = await page.goto(server.PREFIX + '/foo.html');
const redirectedFrom = response.request().redirectedFrom();
expect(redirectedFrom).toBeTruthy();
const redirected = await redirectedFrom.response();
expect(redirected.status()).toBe(302);
let error = null;
await redirected.text().catch(e => error = e);
expect(error.message).toContain('Response body is unavailable for redirect responses');
});
it('should wait until response completes', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
// Setup server to trap request.
let serverResponse = null;
server.setRoute('/get', (req, res) => {
serverResponse = res;
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
// from server.
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.write('hello ');
});
// Setup page to trap response.
let requestFinished = false;
page.on('requestfinished', r => requestFinished = requestFinished || r.url().includes('/get'));
// send request and wait for server response
const [pageResponse] = await Promise.all([
page.waitForEvent('response'),
page.evaluate(() => fetch('./get', { method: 'GET'})),
server.waitForRequest('/get'),
]);
expect(serverResponse).toBeTruthy();
expect(pageResponse).toBeTruthy();
expect(pageResponse.status()).toBe(200);
expect(requestFinished).toBe(false);
const responseText = pageResponse.text();
// Write part of the response and wait for it to be flushed.
await new Promise(x => serverResponse.write('wor', x));
// Finish response.
await new Promise(x => serverResponse.end('ld!', x));
expect(await responseText).toBe('hello world!');
});
it('should return json', async({page, server}) => {
const response = await page.goto(server.PREFIX + '/simple.json');
expect(await response.json()).toEqual({foo: 'bar'});
});
it('should return body', async({page, server}) => {
const response = await page.goto(server.PREFIX + '/pptr.png');
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
const responseBuffer = await response.body();
expect(responseBuffer.equals(imageBuffer)).toBe(true);
});
it('should return body with compression', async({page, server}) => {
server.enableGzip('/pptr.png');
const response = await page.goto(server.PREFIX + '/pptr.png');
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
const responseBuffer = await response.body();
expect(responseBuffer.equals(imageBuffer)).toBe(true);
});
it('should return status text', async({page, server}) => {
server.setRoute('/cool', (req, res) => {
res.writeHead(200, 'cool!');
res.end();
});
const response = await page.goto(server.PREFIX + '/cool');
expect(response.statusText()).toBe('cool!');
});

View file

@ -1,488 +0,0 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
describe('Page.Events.Request', function() {
it('should fire for navigation requests', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
});
it('should fire for iframes', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(2);
});
it('should fire for fetches', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => fetch('/empty.html'));
expect(requests.length).toBe(2);
});
it('should report requests and responses handled by service worker', async({page, server}) => {
await page.goto(server.PREFIX + '/serviceworkers/fetchdummy/sw.html');
await page.evaluate(() => window.activationPromise);
const [swResponse, request] = await Promise.all([
page.evaluate(() => fetchDummy('foo')),
page.waitForEvent('request'),
]);
expect(swResponse).toBe('responseFromServiceWorker:foo');
expect(request.url()).toBe(server.PREFIX + '/serviceworkers/fetchdummy/foo');
const response = await request.response();
expect(response.url()).toBe(server.PREFIX + '/serviceworkers/fetchdummy/foo');
expect(await response.text()).toBe('responseFromServiceWorker:foo');
});
});
describe('Request.frame', function() {
it('should work for main frame navigation request', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame());
});
it('should work for subframe navigation request', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const requests = [];
page.on('request', request => requests.push(request));
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.frames()[1]);
});
it('should work for fetch requests', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
let requests = [];
page.on('request', request => requests.push(request));
await page.evaluate(() => fetch('/digits/1.png'));
requests = requests.filter(request => !request.url().includes('favicon'));
expect(requests.length).toBe(1);
expect(requests[0].frame()).toBe(page.mainFrame());
});
});
describe('Request.headers', function() {
it('should work', async({page, server}) => {
const response = await page.goto(server.EMPTY_PAGE);
if (CHROMIUM)
expect(response.request().headers()['user-agent']).toContain('Chrome');
else if (FFOX)
expect(response.request().headers()['user-agent']).toContain('Firefox');
else if (WEBKIT)
expect(response.request().headers()['user-agent']).toContain('WebKit');
});
it.fail(CHROMIUM||WEBKIT)('should get the same headers as the server', async({page, server}) => {
await page.goto(server.PREFIX + '/empty.html');
let serverRequest;
await server.setRoute('/something', (request, response) => {
serverRequest = request;
response.writeHead(200, { 'Access-Control-Allow-Origin': '*' });
response.end('done');
});
const requestPromise = page.waitForEvent('request');
const text = await page.evaluate(async url => {
const data = await fetch(url);
return data.text();
}, server.CROSS_PROCESS_PREFIX + '/something');
const request = await requestPromise;
expect(text).toBe('done');
expect(request.headers()).toEqual(serverRequest.headers);
});
});
describe('Response.headers', function() {
it('should work', async({page, server}) => {
server.setRoute('/empty.html', (req, res) => {
res.setHeader('foo', 'bar');
res.end();
});
const response = await page.goto(server.EMPTY_PAGE);
expect(response.headers()['foo']).toBe('bar');
});
});
describe('Request.postData', function() {
it('should work', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})}));
expect(request).toBeTruthy();
expect(request.postData()).toBe('{"foo":"bar"}');
});
it('should work with binary', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(async () => {
await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) })
});
expect(request).toBeTruthy();
const buffer = request.postDataBuffer();
expect(buffer.length).toBe(256);
for (let i = 0; i < 256; ++i)
expect(buffer[i]).toBe(i);
});
it('should work with binary and interception', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
await page.route('/post', route => route.continue());
page.on('request', r => request = r);
await page.evaluate(async () => {
await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) })
});
expect(request).toBeTruthy();
const buffer = request.postDataBuffer();
expect(buffer.length).toBe(256);
for (let i = 0; i < 256; ++i)
expect(buffer[i]).toBe(i);
});
it('should be |undefined| when there is no post data', async({page, server}) => {
const response = await page.goto(server.EMPTY_PAGE);
expect(response.request().postData()).toBe(null);
});
it('should parse the JSON payload', async ({ page, server }) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.evaluate(() => fetch('./post', { method: 'POST', body: JSON.stringify({ foo: 'bar' }) }));
expect(request).toBeTruthy();
expect(request.postDataJSON()).toEqual({ "foo": "bar" });
});
it('should parse the data if content-type is application/x-www-form-urlencoded', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
server.setRoute('/post', (req, res) => res.end());
let request = null;
page.on('request', r => request = r);
await page.setContent(`<form method='POST' action='/post'><input type='text' name='foo' value='bar'><input type='number' name='baz' value='123'><input type='submit'></form>`);
await page.click('input[type=submit]');
expect(request).toBeTruthy();
expect(request.postDataJSON()).toEqual({'foo':'bar','baz':'123'});
})
it('should be |undefined| when there is no post data', async ({ page, server }) => {
const response = await page.goto(server.EMPTY_PAGE);
expect(response.request().postDataJSON()).toBe(null);
});
});
describe('Response.text', function() {
it('should work', async({page, server}) => {
const response = await page.goto(server.PREFIX + '/simple.json');
expect(await response.text()).toBe('{"foo": "bar"}\n');
});
it('should return uncompressed text', async({page, server}) => {
server.enableGzip('/simple.json');
const response = await page.goto(server.PREFIX + '/simple.json');
expect(response.headers()['content-encoding']).toBe('gzip');
expect(await response.text()).toBe('{"foo": "bar"}\n');
});
it('should throw when requesting body of redirected response', async({page, server}) => {
server.setRedirect('/foo.html', '/empty.html');
const response = await page.goto(server.PREFIX + '/foo.html');
const redirectedFrom = response.request().redirectedFrom();
expect(redirectedFrom).toBeTruthy();
const redirected = await redirectedFrom.response();
expect(redirected.status()).toBe(302);
let error = null;
await redirected.text().catch(e => error = e);
expect(error.message).toContain('Response body is unavailable for redirect responses');
});
it('should wait until response completes', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
// Setup server to trap request.
let serverResponse = null;
server.setRoute('/get', (req, res) => {
serverResponse = res;
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
// from server.
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.write('hello ');
});
// Setup page to trap response.
let requestFinished = false;
page.on('requestfinished', r => requestFinished = requestFinished || r.url().includes('/get'));
// send request and wait for server response
const [pageResponse] = await Promise.all([
page.waitForEvent('response'),
page.evaluate(() => fetch('./get', { method: 'GET'})),
server.waitForRequest('/get'),
]);
expect(serverResponse).toBeTruthy();
expect(pageResponse).toBeTruthy();
expect(pageResponse.status()).toBe(200);
expect(requestFinished).toBe(false);
const responseText = pageResponse.text();
// Write part of the response and wait for it to be flushed.
await new Promise(x => serverResponse.write('wor', x));
// Finish response.
await new Promise(x => serverResponse.end('ld!', x));
expect(await responseText).toBe('hello world!');
});
});
describe('Response.json', function() {
it('should work', async({page, server}) => {
const response = await page.goto(server.PREFIX + '/simple.json');
expect(await response.json()).toEqual({foo: 'bar'});
});
});
describe('Response.body', function() {
it('should work', async({page, server}) => {
const response = await page.goto(server.PREFIX + '/pptr.png');
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
const responseBuffer = await response.body();
expect(responseBuffer.equals(imageBuffer)).toBe(true);
});
it('should work with compression', async({page, server}) => {
server.enableGzip('/pptr.png');
const response = await page.goto(server.PREFIX + '/pptr.png');
const imageBuffer = fs.readFileSync(path.join(__dirname, 'assets', 'pptr.png'));
const responseBuffer = await response.body();
expect(responseBuffer.equals(imageBuffer)).toBe(true);
});
});
describe('Response.statusText', function() {
it('should work', async({page, server}) => {
server.setRoute('/cool', (req, res) => {
res.writeHead(200, 'cool!');
res.end();
});
const response = await page.goto(server.PREFIX + '/cool');
expect(response.statusText()).toBe('cool!');
});
});
describe('Request.resourceType', function() {
it('should return event source', async ({page, server}) => {
const SSE_MESSAGE = {foo: 'bar'};
// 1. Setup server-sent events on server that immediately sends a message to the client.
server.setRoute('/sse', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
'Cache-Control': 'no-cache',
});
res.write(`data: ${JSON.stringify(SSE_MESSAGE)}\n\n`);
});
// 2. Subscribe to page request events.
await page.goto(server.EMPTY_PAGE);
const requests = [];
page.on('request', request => requests.push(request));
// 3. Connect to EventSource in browser and return first message.
expect(await page.evaluate(() => {
const eventSource = new EventSource('/sse');
return new Promise(resolve => {
eventSource.onmessage = e => resolve(JSON.parse(e.data));
});
})).toEqual(SSE_MESSAGE);
expect(requests[0].resourceType()).toBe('eventsource');
});
});
describe('Network Events', function() {
it('Page.Events.Request', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
expect(requests[0].resourceType()).toBe('document');
expect(requests[0].method()).toBe('GET');
expect(await requests[0].response()).toBeTruthy();
expect(requests[0].frame() === page.mainFrame()).toBe(true);
expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
});
it('Page.Events.Response', async({page, server}) => {
const responses = [];
page.on('response', response => responses.push(response));
await page.goto(server.EMPTY_PAGE);
expect(responses.length).toBe(1);
expect(responses[0].url()).toBe(server.EMPTY_PAGE);
expect(responses[0].status()).toBe(200);
expect(responses[0].ok()).toBe(true);
expect(responses[0].request()).toBeTruthy();
});
it('Page.Events.RequestFailed', async({page, server}) => {
server.setRoute('/one-style.css', (req, res) => {
res.setHeader('Content-Type', 'text/css');
res.socket.destroy();
});
const failedRequests = [];
page.on('requestfailed', request => failedRequests.push(request));
await page.goto(server.PREFIX + '/one-style.html');
expect(failedRequests.length).toBe(1);
expect(failedRequests[0].url()).toContain('one-style.css');
expect(await failedRequests[0].response()).toBe(null);
expect(failedRequests[0].resourceType()).toBe('stylesheet');
if (CHROMIUM) {
expect(failedRequests[0].failure().errorText).toBe('net::ERR_EMPTY_RESPONSE');
} else if (WEBKIT) {
if (MAC)
expect(failedRequests[0].failure().errorText).toBe('The network connection was lost.');
else if (WIN)
expect(failedRequests[0].failure().errorText).toBe('Server returned nothing (no headers, no data)');
else
expect(failedRequests[0].failure().errorText).toBe('Message Corrupt');
} else {
expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_NET_RESET');
}
expect(failedRequests[0].frame()).toBeTruthy();
});
it('Page.Events.RequestFinished', async({page, server}) => {
const [response] = await Promise.all([
page.goto(server.EMPTY_PAGE),
page.waitForEvent('requestfinished')
]);
const request = response.request();
expect(request.url()).toBe(server.EMPTY_PAGE);
expect(await request.response()).toBeTruthy();
expect(request.frame() === page.mainFrame()).toBe(true);
expect(request.frame().url()).toBe(server.EMPTY_PAGE);
expect(request.failure()).toBe(null);
});
it('should fire events in proper order', async({page, server}) => {
const events = [];
page.on('request', request => events.push('request'));
page.on('response', response => events.push('response'));
const response = await page.goto(server.EMPTY_PAGE);
expect(await response.finished()).toBe(null);
events.push('requestfinished')
expect(events).toEqual(['request', 'response', 'requestfinished']);
});
it('should support redirects', async({page, server}) => {
const events = [];
page.on('request', request => events.push(`${request.method()} ${request.url()}`));
page.on('response', response => events.push(`${response.status()} ${response.url()}`));
page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
server.setRedirect('/foo.html', '/empty.html');
const FOO_URL = server.PREFIX + '/foo.html';
const response = await page.goto(FOO_URL);
await response.finished();
expect(events).toEqual([
`GET ${FOO_URL}`,
`302 ${FOO_URL}`,
`DONE ${FOO_URL}`,
`GET ${server.EMPTY_PAGE}`,
`200 ${server.EMPTY_PAGE}`,
`DONE ${server.EMPTY_PAGE}`
]);
const redirectedFrom = response.request().redirectedFrom();
expect(redirectedFrom.url()).toContain('/foo.html');
expect(redirectedFrom.redirectedFrom()).toBe(null);
expect(redirectedFrom.redirectedTo()).toBe(response.request());
});
});
describe('Request.isNavigationRequest', () => {
it('should work', async({page, server}) => {
const requests = new Map();
page.on('request', request => requests.set(request.url().split('/').pop(), request));
server.setRedirect('/rrredirect', '/frames/one-frame.html');
await page.goto(server.PREFIX + '/rrredirect');
expect(requests.get('rrredirect').isNavigationRequest()).toBe(true);
expect(requests.get('one-frame.html').isNavigationRequest()).toBe(true);
expect(requests.get('frame.html').isNavigationRequest()).toBe(true);
expect(requests.get('script.js').isNavigationRequest()).toBe(false);
expect(requests.get('style.css').isNavigationRequest()).toBe(false);
});
it('should work when navigating to image', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.PREFIX + '/pptr.png');
expect(requests[0].isNavigationRequest()).toBe(true);
});
});
describe('Page.setExtraHTTPHeaders', function() {
it('should work', async({page, server}) => {
await page.setExtraHTTPHeaders({
foo: 'bar'
});
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.EMPTY_PAGE),
]);
expect(request.headers['foo']).toBe('bar');
});
it('should work with redirects', async({page, server}) => {
server.setRedirect('/foo.html', '/empty.html');
await page.setExtraHTTPHeaders({
foo: 'bar'
});
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.PREFIX + '/foo.html'),
]);
expect(request.headers['foo']).toBe('bar');
});
it('should work with extra headers from browser context', async({browser, server}) => {
const context = await browser.newContext();
await context.setExtraHTTPHeaders({
'foo': 'bar',
});
const page = await context.newPage();
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.EMPTY_PAGE),
]);
await context.close();
expect(request.headers['foo']).toBe('bar');
});
it('should override extra headers from browser context', async({browser, server}) => {
const context = await browser.newContext({
extraHTTPHeaders: { 'fOo': 'bAr', 'baR': 'foO' },
});
const page = await context.newPage();
await page.setExtraHTTPHeaders({
'Foo': 'Bar'
});
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.EMPTY_PAGE),
]);
await context.close();
expect(request.headers['foo']).toBe('Bar');
expect(request.headers['bar']).toBe('foO');
});
it('should throw for non-string header values', async({page, server}) => {
let error = null;
try {
await page.setExtraHTTPHeaders({ 'foo': 1 });
} catch (e) {
error = e;
}
expect(error.message).toContain('Expected value of header "foo" to be String, but "number" is found.');
});
});

View file

@ -0,0 +1,109 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('should work', async({page, server}) => {
const windowHandle = await page.evaluateHandle(() => window);
expect(windowHandle).toBeTruthy();
});
it('should accept object handle as an argument', async({page, server}) => {
const navigatorHandle = await page.evaluateHandle(() => navigator);
const text = await page.evaluate(e => e.userAgent, navigatorHandle);
expect(text).toContain('Mozilla');
});
it('should accept object handle to primitive types', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => 5);
const isFive = await page.evaluate(e => Object.is(e, 5), aHandle);
expect(isFive).toBeTruthy();
});
it('should accept nested handle', async({page, server}) => {
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
const result = await page.evaluate(({ foo }) => {
return foo;
}, { foo });
expect(result).toEqual({ x: 1, y: 'foo' });
});
it('should accept nested window handle', async({page, server}) => {
const foo = await page.evaluateHandle(() => window);
const result = await page.evaluate(({ foo }) => {
return foo === window;
}, { foo });
expect(result).toBe(true);
});
it('should accept multiple nested handles', async({page, server}) => {
const foo = await page.evaluateHandle(() => ({ x: 1, y: 'foo' }));
const bar = await page.evaluateHandle(() => 5);
const baz = await page.evaluateHandle(() => (['baz']));
const result = await page.evaluate(x => {
return JSON.stringify(x);
}, { a1: { foo }, a2: { bar, arr: [{ baz }] } });
expect(JSON.parse(result)).toEqual({
a1: { foo: { x: 1, y: 'foo' } },
a2: { bar: 5, arr: [{ baz: ['baz'] }] }
});
});
it('should throw for circular objects', async({page, server}) => {
const a = { x: 1 };
a.y = a;
const error = await page.evaluate(x => x, a).catch(e => e);
expect(error.message).toContain('Argument is a circular structure');
});
it('should accept same handle multiple times', async({page, server}) => {
const foo = await page.evaluateHandle(() => 1);
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: 1, bar: [1], baz: { foo: 1 } });
});
it('should accept same nested object multiple times', async({page, server}) => {
const foo = { x: 1 };
expect(await page.evaluate(x => x, { foo, bar: [foo], baz: { foo }})).toEqual({ foo: { x: 1 }, bar: [{ x : 1 }], baz: { foo: { x : 1 } } });
});
it('should accept object handle to unserializable value', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => Infinity);
expect(await page.evaluate(e => Object.is(e, Infinity), aHandle)).toBe(true);
});
it('should pass configurable args', async({page, server}) => {
const result = await page.evaluate(arg => {
if (arg.foo !== 42)
throw new Error('Not a 42');
arg.foo = 17;
if (arg.foo !== 17)
throw new Error('Not 17');
delete arg.foo;
if (arg.foo === 17)
throw new Error('Still 17');
return arg;
}, { foo: 42 });
expect(result).toEqual({});
});
it('should work with primitives', async({page, server}) => {
const aHandle = await page.evaluateHandle(() => {
window.FOO = 123;
return window;
});
expect(await page.evaluate(e => e.FOO, aHandle)).toBe(123);
});

View file

@ -0,0 +1,119 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
it('Page.Events.Request', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
expect(requests[0].url()).toBe(server.EMPTY_PAGE);
expect(requests[0].resourceType()).toBe('document');
expect(requests[0].method()).toBe('GET');
expect(await requests[0].response()).toBeTruthy();
expect(requests[0].frame() === page.mainFrame()).toBe(true);
expect(requests[0].frame().url()).toBe(server.EMPTY_PAGE);
});
it('Page.Events.Response', async({page, server}) => {
const responses = [];
page.on('response', response => responses.push(response));
await page.goto(server.EMPTY_PAGE);
expect(responses.length).toBe(1);
expect(responses[0].url()).toBe(server.EMPTY_PAGE);
expect(responses[0].status()).toBe(200);
expect(responses[0].ok()).toBe(true);
expect(responses[0].request()).toBeTruthy();
});
it('Page.Events.RequestFailed', async({page, server}) => {
server.setRoute('/one-style.css', (req, res) => {
res.setHeader('Content-Type', 'text/css');
res.socket.destroy();
});
const failedRequests = [];
page.on('requestfailed', request => failedRequests.push(request));
await page.goto(server.PREFIX + '/one-style.html');
expect(failedRequests.length).toBe(1);
expect(failedRequests[0].url()).toContain('one-style.css');
expect(await failedRequests[0].response()).toBe(null);
expect(failedRequests[0].resourceType()).toBe('stylesheet');
if (CHROMIUM) {
expect(failedRequests[0].failure().errorText).toBe('net::ERR_EMPTY_RESPONSE');
} else if (WEBKIT) {
if (MAC)
expect(failedRequests[0].failure().errorText).toBe('The network connection was lost.');
else if (WIN)
expect(failedRequests[0].failure().errorText).toBe('Server returned nothing (no headers, no data)');
else
expect(failedRequests[0].failure().errorText).toBe('Message Corrupt');
} else {
expect(failedRequests[0].failure().errorText).toBe('NS_ERROR_NET_RESET');
}
expect(failedRequests[0].frame()).toBeTruthy();
});
it('Page.Events.RequestFinished', async({page, server}) => {
const [response] = await Promise.all([
page.goto(server.EMPTY_PAGE),
page.waitForEvent('requestfinished')
]);
const request = response.request();
expect(request.url()).toBe(server.EMPTY_PAGE);
expect(await request.response()).toBeTruthy();
expect(request.frame() === page.mainFrame()).toBe(true);
expect(request.frame().url()).toBe(server.EMPTY_PAGE);
expect(request.failure()).toBe(null);
});
it('should fire events in proper order', async({page, server}) => {
const events = [];
page.on('request', request => events.push('request'));
page.on('response', response => events.push('response'));
const response = await page.goto(server.EMPTY_PAGE);
expect(await response.finished()).toBe(null);
events.push('requestfinished')
expect(events).toEqual(['request', 'response', 'requestfinished']);
});
it('should support redirects', async({page, server}) => {
const events = [];
page.on('request', request => events.push(`${request.method()} ${request.url()}`));
page.on('response', response => events.push(`${response.status()} ${response.url()}`));
page.on('requestfinished', request => events.push(`DONE ${request.url()}`));
page.on('requestfailed', request => events.push(`FAIL ${request.url()}`));
server.setRedirect('/foo.html', '/empty.html');
const FOO_URL = server.PREFIX + '/foo.html';
const response = await page.goto(FOO_URL);
await response.finished();
expect(events).toEqual([
`GET ${FOO_URL}`,
`302 ${FOO_URL}`,
`DONE ${FOO_URL}`,
`GET ${server.EMPTY_PAGE}`,
`200 ${server.EMPTY_PAGE}`,
`DONE ${server.EMPTY_PAGE}`
]);
const redirectedFrom = response.request().redirectedFrom();
expect(redirectedFrom.url()).toContain('/foo.html');
expect(redirectedFrom.redirectedFrom()).toBe(null);
expect(redirectedFrom.redirectedTo()).toBe(response.request());
});

View file

@ -0,0 +1,58 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
it('should fire for navigation requests', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
expect(requests.length).toBe(1);
});
it('should fire for iframes', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
expect(requests.length).toBe(2);
});
it('should fire for fetches', async({page, server}) => {
const requests = [];
page.on('request', request => requests.push(request));
await page.goto(server.EMPTY_PAGE);
await page.evaluate(() => fetch('/empty.html'));
expect(requests.length).toBe(2);
});
it('should report requests and responses handled by service worker', async({page, server}) => {
await page.goto(server.PREFIX + '/serviceworkers/fetchdummy/sw.html');
await page.evaluate(() => window.activationPromise);
const [swResponse, request] = await Promise.all([
page.evaluate(() => fetchDummy('foo')),
page.waitForEvent('request'),
]);
expect(swResponse).toBe('responseFromServiceWorker:foo');
expect(request.url()).toBe(server.PREFIX + '/serviceworkers/fetchdummy/foo');
const response = await request.response();
expect(response.url()).toBe(server.PREFIX + '/serviceworkers/fetchdummy/foo');
expect(await response.text()).toBe('responseFromServiceWorker:foo');
});

View file

@ -0,0 +1,249 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, HEADLESS} = testOptions;
const {PNG} = require('pngjs');
// Firefox headful produces a different image.
const ffheadful = FFOX && !HEADLESS;
it.skip(ffheadful)('should work', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-sanity.png');
});
it.skip(ffheadful)('should clip rect', async({page, server}) => {
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).toBeGolden('screenshot-clip-rect.png');
});
it.skip(ffheadful)('should clip rect with fullPage', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(150, 200));
const screenshot = await page.screenshot({
fullPage: true,
clip: {
x: 50,
y: 100,
width: 150,
height: 100,
},
});
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
});
it.skip(ffheadful)('should clip elements to the viewport', async({page, server}) => {
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).toBeGolden('screenshot-offscreen-clip.png');
});
it.skip(ffheadful)('should throw on clip outside the viewport', async({page, server}) => {
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}) => {
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]).toBeGolden('grid-cell-1.png');
});
it.skip(ffheadful)('should take fullPage screenshots', 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).toBeGolden('screenshot-grid-fullpage.png');
});
it.skip(ffheadful)('should restore viewport after fullPage screenshot', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeInstanceOf(Buffer);
await utils.verifyViewport(page, 500, 500);
});
it.skip(ffheadful)('should run in parallel in multiple pages', async({page, server, context}) => {
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]).toBeGolden(`grid-cell-${i % 2}.png`);
await Promise.all(pages.map(page => page.close()));
});
it.fail(FFOX)('should allow transparency', async({page}) => {
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).toBeGolden('transparent.png');
});
it.skip(ffheadful)('should render white background on jpeg file', async({page, server}) => {
await page.setViewportSize({ width: 100, height: 100 });
await page.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
expect(screenshot).toBeGolden('white.jpg');
});
it.skip(ffheadful)('should work with odd clip size on Retina displays', async({page}) => {
const screenshot = await page.screenshot({
clip: {
x: 0,
y: 0,
width: 11,
height: 11,
}
});
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
});
it.skip(FFOX)('should work with a mobile viewport', 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 + '/overflow.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-mobile.png');
await context.close();
});
it.skip(FFOX)('should work with a mobile viewport and clip', 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 + '/overflow.html');
const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } });
expect(screenshot).toBeGolden('screenshot-mobile-clip.png');
await context.close();
});
it.skip(FFOX)('should work with a mobile viewport and fullPage', 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 + '/overflow-large.html');
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeGolden('screenshot-mobile-fullpage.png');
await context.close();
});
it.skip(ffheadful)('should work for canvas', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/screenshots/canvas.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-canvas.png');
});
it.skip(ffheadful)('should work for translateZ', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/screenshots/translateZ.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-translateZ.png');
});
it.fail(FFOX || WEBKIT)('should work for webgl', async({page, server}) => {
await page.setViewportSize({width: 640, height: 480});
await page.goto(server.PREFIX + '/screenshots/webgl.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('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}) => {
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).toBeGolden('screenshot-device-scale-factor.png');
await context.close();
});
it.skip(ffheadful)('should work with iframe in shadow', async({browser, page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html');
expect(await page.screenshot()).toBeGolden('screenshot-iframe.png');
});

View file

@ -0,0 +1,86 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, MAC, WIN} = testOptions;
it('should work', async({page, server}) => {
await page.setExtraHTTPHeaders({
foo: 'bar'
});
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.EMPTY_PAGE),
]);
expect(request.headers['foo']).toBe('bar');
});
it('should work with redirects', async({page, server}) => {
server.setRedirect('/foo.html', '/empty.html');
await page.setExtraHTTPHeaders({
foo: 'bar'
});
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.PREFIX + '/foo.html'),
]);
expect(request.headers['foo']).toBe('bar');
});
it('should work with extra headers from browser context', async({browser, server}) => {
const context = await browser.newContext();
await context.setExtraHTTPHeaders({
'foo': 'bar',
});
const page = await context.newPage();
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.EMPTY_PAGE),
]);
await context.close();
expect(request.headers['foo']).toBe('bar');
});
it('should override extra headers from browser context', async({browser, server}) => {
const context = await browser.newContext({
extraHTTPHeaders: { 'fOo': 'bAr', 'baR': 'foO' },
});
const page = await context.newPage();
await page.setExtraHTTPHeaders({
'Foo': 'Bar'
});
const [request] = await Promise.all([
server.waitForRequest('/empty.html'),
page.goto(server.EMPTY_PAGE),
]);
await context.close();
expect(request.headers['foo']).toBe('Bar');
expect(request.headers['bar']).toBe('foO');
});
it('should throw for non-string header values', async({page, server}) => {
let error = null;
try {
await page.setExtraHTTPHeaders({ 'foo': 1 });
} catch (e) {
error = e;
}
expect(error.message).toContain('Expected value of header "foo" to be String, but "number" is found.');
});

View file

@ -1,558 +0,0 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, HEADLESS} = testOptions;
const {PNG} = require('pngjs');
// Firefox headful produces a different image.
const ffheadful = FFOX && !HEADLESS;
describe.skip(ffheadful)('Page.screenshot', function() {
it('should work', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-sanity.png');
});
it('should clip rect', async({page, server}) => {
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).toBeGolden('screenshot-clip-rect.png');
});
it('should clip rect with fullPage', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(150, 200));
const screenshot = await page.screenshot({
fullPage: true,
clip: {
x: 50,
y: 100,
width: 150,
height: 100,
},
});
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
});
it('should clip elements to the viewport', async({page, server}) => {
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).toBeGolden('screenshot-offscreen-clip.png');
});
it('should throw on clip outside the viewport', async({page, server}) => {
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}) => {
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]).toBeGolden('grid-cell-1.png');
});
it('should take fullPage screenshots', 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).toBeGolden('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({page, server, context}) => {
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]).toBeGolden(`grid-cell-${i % 2}.png`);
await Promise.all(pages.map(page => page.close()));
});
it.fail(FFOX)('should allow transparency', async({page}) => {
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).toBeGolden('transparent.png');
});
it('should render white background on jpeg file', async({page, server}) => {
await page.setViewportSize({ width: 100, height: 100 });
await page.goto(server.EMPTY_PAGE);
const screenshot = await page.screenshot({omitBackground: true, type: 'jpeg'});
expect(screenshot).toBeGolden('white.jpg');
});
it('should work with odd clip size on Retina displays', async({page}) => {
const screenshot = await page.screenshot({
clip: {
x: 0,
y: 0,
width: 11,
height: 11,
}
});
expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
});
it.skip(FFOX)('should work with a mobile viewport', 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 + '/overflow.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-mobile.png');
await context.close();
});
it.skip(FFOX)('should work with a mobile viewport and clip', 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 + '/overflow.html');
const screenshot = await page.screenshot({ clip: { x: 10, y: 10, width: 100, height: 150 } });
expect(screenshot).toBeGolden('screenshot-mobile-clip.png');
await context.close();
});
it.skip(FFOX)('should work with a mobile viewport and fullPage', 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 + '/overflow-large.html');
const screenshot = await page.screenshot({ fullPage: true });
expect(screenshot).toBeGolden('screenshot-mobile-fullpage.png');
await context.close();
});
it('should work for canvas', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/screenshots/canvas.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-canvas.png');
});
it('should work for translateZ', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/screenshots/translateZ.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-translateZ.png');
});
it.fail(FFOX || WEBKIT)('should work for webgl', async({page, server}) => {
await page.setViewportSize({width: 640, height: 480});
await page.goto(server.PREFIX + '/screenshots/webgl.html');
const screenshot = await page.screenshot();
expect(screenshot).toBeGolden('screenshot-webgl.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}) => {
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).toBeGolden('screenshot-device-scale-factor.png');
await context.close();
});
it('should work with iframe in shadow', async({browser, page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid-iframe-in-shadow.html');
expect(await page.screenshot()).toBeGolden('screenshot-iframe.png');
});
});
describe.skip(ffheadful)('ElementHandle.screenshot', function() {
it('should work', async({page, server}) => {
await page.setViewportSize({width: 500, height: 500});
await page.goto(server.PREFIX + '/grid.html');
await page.evaluate(() => window.scrollBy(50, 100));
const elementHandle = await page.$('.box:nth-of-type(3)');
const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('screenshot-element-bounding-box.png');
});
it('should take into account padding and border', async({page}) => {
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).toBeGolden('screenshot-element-padding-border.png');
});
it('should capture full element when larger than viewport in parallel', async({page}) => {
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]).toBeGolden('screenshot-element-larger-than-viewport.png');
await utils.verifyViewport(page, 500, 500);
});
it('should capture full element when larger than viewport', async({page}) => {
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).toBeGolden('screenshot-element-larger-than-viewport.png');
await utils.verifyViewport(page, 500, 500);
});
it('should scroll element into view', async({page}) => {
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).toBeGolden('screenshot-element-scrolled-into-view.png');
});
it('should scroll 15000px into view', async({page}) => {
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).toBeGolden('screenshot-element-scrolled-into-view.png');
});
it('should work with a rotated element', async({page}) => {
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).toBeGolden('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}) => {
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).toBeGolden('screenshot-element-bounding-box.png');
});
it('should work for an element with fractional dimensions', async({page}) => {
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).toBeGolden('screenshot-element-fractional.png');
});
it.skip(FFOX)('should work with a mobile viewport', 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 + '/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).toBeGolden('screenshot-element-mobile.png');
await context.close();
});
it.skip(FFOX)('should work with device scale factor', async({browser, server}) => {
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).toBeGolden('screenshot-element-mobile-dsf.png');
await context.close();
});
it('should work for an element with an offset', async({page}) => {
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).toBeGolden('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.skip(USES_HOOKS)('should restore viewport after page screenshot and exception', async({ browser, server }) => {
const context = await browser.newContext({ viewport: { width: 350, height: 360 } });
const page = await context.newPage();
await page.goto(server.PREFIX + '/grid.html');
const __testHookBeforeScreenshot = () => { throw new Error('oh my') };
const error = await page.screenshot({ fullPage: true, __testHookBeforeScreenshot }).catch(e => e);
expect(error.message).toContain('oh my');
await utils.verifyViewport(page, 350, 360);
await context.close();
});
it.skip(USES_HOOKS)('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 }).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({ viewport: null });
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(USES_HOOKS)('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 }).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', async({page, server}) => {
await page.setViewportSize({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
const elementHandle = await page.$('.box:nth-of-type(3)');
await elementHandle.evaluate(e => {
e.classList.add('animation');
return new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f)));
});
const screenshot = await elementHandle.screenshot();
expect(screenshot).toBeGolden('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);
});
});

View file

@ -0,0 +1,210 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
it('should timeout', async({page, server}) => {
const startTime = Date.now();
const timeout = 42;
await page.waitForTimeout(timeout);
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
});
it('should accept a string', async({page, server}) => {
const watchdog = page.waitForFunction('window.__FOO === 1');
await page.evaluate(() => window.__FOO = 1);
await watchdog;
});
it('should work when resolved right before execution context disposal', async({page, server}) => {
await page.addInitScript(() => window.__RELOADED = true);
await page.waitForFunction(() => {
if (!window.__RELOADED)
window.location.reload();
return true;
});
});
it('should poll on interval', async({page, server}) => {
const polling = 100;
const timeDelta = await page.waitForFunction(() => {
if (!window.__startTime) {
window.__startTime = Date.now();
return false;
}
return Date.now() - window.__startTime;
}, {}, {polling});
expect(await timeDelta.jsonValue()).not.toBeLessThan(polling);
});
it('should avoid side effects after timeout', async({page, server}) => {
let counter = 0;
page.on('console', () => ++counter);
const error = await page.waitForFunction(() => {
window.counter = (window.counter || 0) + 1;
console.log(window.counter);
}, {}, { polling: 1, timeout: 1000 }).catch(e => e);
const savedCounter = counter;
await page.waitForTimeout(2000); // Give it some time to produce more logs.
expect(error.message).toContain('page.waitForFunction: Timeout 1000ms exceeded');
expect(counter).toBe(savedCounter);
});
it('should throw on polling:mutation', async({page, server}) => {
const error = await page.waitForFunction(() => true, {}, {polling: 'mutation'}).catch(e => e);
expect(error.message).toContain('Unknown polling option: mutation');
});
it('should poll on raf', async({page, server}) => {
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'});
await page.evaluate(() => window.__FOO = 'hit');
await watchdog;
});
it('should fail with predicate throwing on first call', async({page, server}) => {
const error = await page.waitForFunction(() => { throw new Error('oh my'); }).catch(e => e);
expect(error.message).toContain('oh my');
});
it('should fail with predicate throwing sometimes', async({page, server}) => {
const error = await page.waitForFunction(() => {
window.counter = (window.counter || 0) + 1;
if (window.counter === 3)
throw new Error('Bad counter!');
return window.counter === 5 ? 'result' : false;
}).catch(e => e);
expect(error.message).toContain('Bad counter!');
});
it('should fail with ReferenceError on wrong page', async({page, server}) => {
const error = await page.waitForFunction(() => globalVar === 123).catch(e => e);
expect(error.message).toContain('globalVar');
});
it('should work with strict CSP policy', async({page, server}) => {
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
await page.goto(server.EMPTY_PAGE);
let error = null;
await Promise.all([
page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'}).catch(e => error = e),
page.evaluate(() => window.__FOO = 'hit')
]);
expect(error).toBe(null);
});
it('should throw on bad polling value', async({page, server}) => {
let error = null;
try {
await page.waitForFunction(() => !!document.body, {}, {polling: 'unknown'});
} catch (e) {
error = e;
}
expect(error).toBeTruthy();
expect(error.message).toContain('polling');
});
it('should throw negative polling interval', async({page, server}) => {
let error = null;
try {
await page.waitForFunction(() => !!document.body, {}, {polling: -10});
} catch (e) {
error = e;
}
expect(error).toBeTruthy();
expect(error.message).toContain('Cannot poll with non-positive interval');
});
it('should return the success value as a JSHandle', async({page}) => {
expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
});
it('should return the window as a success value', async({ page }) => {
expect(await page.waitForFunction(() => window)).toBeTruthy();
});
it('should accept ElementHandle arguments', async({page}) => {
await page.setContent('<div></div>');
const div = await page.$('div');
let resolved = false;
const waitForFunction = page.waitForFunction(element => !element.parentElement, div).then(() => resolved = true);
expect(resolved).toBe(false);
await page.evaluate(element => element.remove(), div);
await waitForFunction;
});
it('should respect timeout', async({page, playwright}) => {
let error = null;
await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForFunction: Timeout 10ms exceeded');
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
});
it('should respect default timeout', async({page, playwright}) => {
page.setDefaultTimeout(1);
let error = null;
await page.waitForFunction('false').catch(e => error = e);
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
expect(error.message).toContain('page.waitForFunction: Timeout 1ms exceeded');
});
it('should disable timeout when its set to 0', async({page}) => {
const watchdog = page.waitForFunction(() => {
window.__counter = (window.__counter || 0) + 1;
return window.__injected;
}, {}, {timeout: 0, polling: 10});
await page.waitForFunction(() => window.__counter > 10);
await page.evaluate(() => window.__injected = true);
await watchdog;
});
it('should survive cross-process navigation', async({page, server}) => {
let fooFound = false;
const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true);
await page.goto(server.EMPTY_PAGE);
expect(fooFound).toBe(false);
await page.reload();
expect(fooFound).toBe(false);
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
expect(fooFound).toBe(false);
await page.evaluate(() => window.__FOO = 1);
await waitForFunction;
expect(fooFound).toBe(true);
});
it('should survive navigations', async({page, server}) => {
const watchdog = page.waitForFunction(() => window.__done);
await page.goto(server.EMPTY_PAGE);
await page.goto(server.PREFIX + '/consolelog.html');
await page.evaluate(() => window.__done = true);
await watchdog;
});
it('should work with multiline body', async({page, server}) => {
const result = await page.waitForFunction(`
(() => true)()
`);
expect(await result.jsonValue()).toBe(true);
});
it('should wait for predicate with arguments', async({page, server}) => {
await page.waitForFunction(({arg1, arg2}) => arg1 + arg2 === 3, { arg1: 1, arg2: 2});
});

View file

@ -0,0 +1,417 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
async function giveItTimeToLog(frame) {
await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
}
const addElement = tag => document.body.appendChild(document.createElement(tag));
it('should throw on waitFor', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
let error;
await page.waitForSelector('*', { waitFor: 'attached' }).catch(e => error = e);
expect(error.message).toContain('options.waitFor is not supported, did you mean options.state?');
});
it('should tolerate waitFor=visible', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.waitForSelector('*', { waitFor: 'visible' }).catch(e => error = e);
});
it('should immediately resolve promise if node exists', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
await frame.waitForSelector('*');
await frame.evaluate(addElement, 'div');
await frame.waitForSelector('div', { state: 'attached'});
});
it('should work with removed MutationObserver', async({page, server}) => {
await page.evaluate(() => delete window.MutationObserver);
const [handle] = await Promise.all([
page.waitForSelector('.zombo'),
page.setContent(`<div class='zombo'>anything</div>`),
]);
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
});
it('should resolve promise when node is added', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('div', { state: 'attached' });
await frame.evaluate(addElement, 'br');
await frame.evaluate(addElement, 'div');
const eHandle = await watchdog;
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
expect(tagName).toBe('DIV');
});
it('should report logs while waiting for visible', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('div', { timeout: 5000 });
await frame.evaluate(() => {
const div = document.createElement('div');
div.className = 'foo bar';
div.id = 'mydiv';
div.setAttribute('style', 'display: none');
div.setAttribute('foo', '123456789012345678901234567890123456789012345678901234567890');
div.textContent = 'abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxz';
document.body.appendChild(div);
});
await giveItTimeToLog(frame);
await frame.evaluate(() => document.querySelector('div').remove());
await giveItTimeToLog(frame);
await frame.evaluate(() => {
const div = document.createElement('div');
div.className = 'another';
div.style.display = 'none';
document.body.appendChild(div);
});
await giveItTimeToLog(frame);
const error = await watchdog.catch(e => e);
expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`waiting for selector "div" to be visible`);
expect(error.message).toContain(`selector resolved to hidden <div id="mydiv" class="foo bar" foo="1234567890123456…>abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvw…</div>`);
expect(error.message).toContain(`selector did not resolve to any element`);
expect(error.message).toContain(`selector resolved to hidden <div class="another"></div>`);
});
it('should report logs while waiting for hidden', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
await frame.evaluate(() => {
const div = document.createElement('div');
div.className = 'foo bar';
div.id = 'mydiv';
div.textContent = 'hello';
document.body.appendChild(div);
});
const watchdog = frame.waitForSelector('div', { state: 'hidden', timeout: 5000 });
await giveItTimeToLog(frame);
await frame.evaluate(() => {
document.querySelector('div').remove();
const div = document.createElement('div');
div.className = 'another';
div.textContent = 'hello';
document.body.appendChild(div);
});
await giveItTimeToLog(frame);
const error = await watchdog.catch(e => e);
expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`waiting for selector "div" to be hidden`);
expect(error.message).toContain(`selector resolved to visible <div id="mydiv" class="foo bar">hello</div>`);
expect(error.message).toContain(`selector resolved to visible <div class="another">hello</div>`);
});
it('should resolve promise when node is added in shadow dom', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const watchdog = page.waitForSelector('span');
await page.evaluate(() => {
const div = document.createElement('div');
div.attachShadow({mode: 'open'});
document.body.appendChild(div);
});
await page.evaluate(() => new Promise(f => setTimeout(f, 100)));
await page.evaluate(() => {
const span = document.createElement('span');
span.textContent = 'Hello from shadow';
document.querySelector('div').shadowRoot.appendChild(span);
});
const handle = await watchdog;
expect(await handle.evaluate(e => e.textContent)).toBe('Hello from shadow');
});
it('should work when node is added through innerHTML', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const watchdog = page.waitForSelector('h3 div', { state: 'attached'});
await page.evaluate(addElement, 'span');
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
await watchdog;
});
it('Page.$ waitFor is shortcut for main frame', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const otherFrame = page.frames()[1];
const watchdog = page.waitForSelector('div', { state: 'attached' });
await otherFrame.evaluate(addElement, 'div');
await page.evaluate(addElement, 'div');
const eHandle = await watchdog;
expect(await eHandle.ownerFrame()).toBe(page.mainFrame());
});
it('should run in specified frame', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1];
const frame2 = page.frames()[2];
const waitForSelectorPromise = frame2.waitForSelector('div', { state: 'attached' });
await frame1.evaluate(addElement, 'div');
await frame2.evaluate(addElement, 'div');
const eHandle = await waitForSelectorPromise;
expect(await eHandle.ownerFrame()).toBe(frame2);
});
it('should throw when frame is detached', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1];
let waitError = null;
const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
await utils.detachFrame(page, 'frame1');
await waitPromise;
expect(waitError).toBeTruthy();
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
});
it('should survive cross-process navigation', async({page, server}) => {
let boxFound = false;
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
await page.goto(server.EMPTY_PAGE);
expect(boxFound).toBe(false);
await page.reload();
expect(boxFound).toBe(false);
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
await waitForSelector;
expect(boxFound).toBe(true);
});
it('should wait for visible', async({page, server}) => {
let divFound = false;
const waitForSelector = page.waitForSelector('div').then(() => divFound = true);
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
expect(divFound).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
expect(divFound).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
expect(await waitForSelector).toBe(true);
expect(divFound).toBe(true);
});
it('should not consider visible when zero-sized', async({page, server}) => {
await page.setContent(`<div style='width: 0; height: 0;'>1</div>`);
let error = await page.waitForSelector('div', { timeout: 1000 }).catch(e => e);
expect(error.message).toContain('page.waitForSelector: Timeout 1000ms exceeded');
await page.evaluate(() => document.querySelector('div').style.width = '10px');
error = await page.waitForSelector('div', { timeout: 1000 }).catch(e => e);
expect(error.message).toContain('page.waitForSelector: Timeout 1000ms exceeded');
await page.evaluate(() => document.querySelector('div').style.height = '10px');
expect(await page.waitForSelector('div', { timeout: 1000 })).toBeTruthy();
});
it('should wait for visible recursively', async({page, server}) => {
let divVisible = false;
const waitForSelector = page.waitForSelector('div#inner').then(() => divVisible = true);
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
expect(divVisible).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
expect(divVisible).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
expect(await waitForSelector).toBe(true);
expect(divVisible).toBe(true);
});
it('hidden should wait for hidden', async({page, server}) => {
let divHidden = false;
await page.setContent(`<div style='display: block;'>content</div>`);
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divHidden = true);
await page.waitForSelector('div'); // do a round trip
expect(divHidden).toBe(false);
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
expect(await waitForSelector).toBe(true);
expect(divHidden).toBe(true);
});
it('hidden should wait for display: none', async({page, server}) => {
let divHidden = false;
await page.setContent(`<div style='display: block;'>content</div>`);
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divHidden = true);
await page.waitForSelector('div'); // do a round trip
expect(divHidden).toBe(false);
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
expect(await waitForSelector).toBe(true);
expect(divHidden).toBe(true);
});
it('hidden should wait for removal', async({page, server}) => {
await page.setContent(`<div>content</div>`);
let divRemoved = false;
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divRemoved = true);
await page.waitForSelector('div'); // do a round trip
expect(divRemoved).toBe(false);
await page.evaluate(() => document.querySelector('div').remove());
expect(await waitForSelector).toBe(true);
expect(divRemoved).toBe(true);
});
it('should return null if waiting to hide non-existing element', async({page, server}) => {
const handle = await page.waitForSelector('non-existing', { state: 'hidden' });
expect(handle).toBe(null);
});
it('should respect timeout', async({page, playwright}) => {
let error = null;
await page.waitForSelector('div', { timeout: 3000, state: 'attached' }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForSelector: Timeout 3000ms exceeded');
expect(error.message).toContain('waiting for selector "div"');
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
});
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
await page.setContent(`<div>content</div>`);
let error = null;
await page.waitForSelector('div', { state: 'hidden', timeout: 1000 }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForSelector: Timeout 1000ms exceeded');
expect(error.message).toContain('waiting for selector "div" to be hidden');
});
it('should respond to node attribute mutation', async({page, server}) => {
let divFound = false;
const waitForSelector = page.waitForSelector('.zombo', { state: 'attached'}).then(() => divFound = true);
await page.setContent(`<div class='notZombo'></div>`);
expect(divFound).toBe(false);
await page.evaluate(() => document.querySelector('div').className = 'zombo');
expect(await waitForSelector).toBe(true);
});
it('should return the element handle', async({page, server}) => {
const waitForSelector = page.waitForSelector('.zombo');
await page.setContent(`<div class='zombo'>anything</div>`);
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
});
it('should have correct stack trace for timeout', async({page, server}) => {
let error;
await page.waitForSelector('.zombo', { timeout: 10 }).catch(e => error = e);
expect(error.stack).toContain('wait-for-selector');
});
it('should throw for unknown state option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { state: 'foo' }).catch(e => e);
expect(error.message).toContain('state: expected one of (attached|detached|visible|hidden)');
});
it('should throw for visibility option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
expect(error.message).toContain('options.visibility is not supported, did you mean options.state?');
});
it('should throw for true state option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { state: true }).catch(e => e);
expect(error.message).toContain('state: expected one of (attached|detached|visible|hidden)');
});
it('should throw for false state option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { state: false }).catch(e => e);
expect(error.message).toContain('state: expected one of (attached|detached|visible|hidden)');
});
it('should support >> selector syntax', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('css=div >> css=span', { state: 'attached'});
await frame.evaluate(addElement, 'br');
await frame.evaluate(addElement, 'div');
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
const eHandle = await watchdog;
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
expect(tagName).toBe('SPAN');
});
it('should wait for detached if already detached', async({page, server}) => {
await page.setContent('<section id="testAttribute">43543</section>');
expect(await page.waitForSelector('css=div', { state: 'detached'})).toBe(null);
});
it('should wait for detached', async({page, server}) => {
await page.setContent('<section id="testAttribute"><div>43543</div></section>');
let done = false;
const waitFor = page.waitForSelector('css=div', { state: 'detached'}).then(() => done = true);
expect(done).toBe(false);
await page.waitForSelector('css=section');
expect(done).toBe(false);
await page.$eval('div', div => div.remove());
expect(await waitFor).toBe(true);
expect(done).toBe(true);
});
it('should support some fancy xpath', async({page, server}) => {
await page.setContent(`<p>red herring</p><p>hello world </p>`);
const waitForXPath = page.waitForSelector('//p[normalize-space(.)="hello world"]');
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
});
it('should respect timeout xpath', async({page, playwright}) => {
let error = null;
await page.waitForSelector('//div', { state: 'attached', timeout: 3000 }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForSelector: Timeout 3000ms exceeded');
expect(error.message).toContain('waiting for selector "//div"');
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
});
it('should run in specified frame xpath', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1];
const frame2 = page.frames()[2];
const waitForXPathPromise = frame2.waitForSelector('//div', { state: 'attached' });
await frame1.evaluate(addElement, 'div');
await frame2.evaluate(addElement, 'div');
const eHandle = await waitForXPathPromise;
expect(await eHandle.ownerFrame()).toBe(frame2);
});
it('should throw when frame is detached xpath', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1];
let waitError = null;
const waitPromise = frame.waitForSelector('//*[@class="box"]').catch(e => waitError = e);
await utils.detachFrame(page, 'frame1');
await waitPromise;
expect(waitError).toBeTruthy();
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
});
it('should return the element handle xpath', async({page, server}) => {
const waitForXPath = page.waitForSelector('//*[@class="zombo"]');
await page.setContent(`<div class='zombo'>anything</div>`);
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
});
it('should allow you to select an element with single slash xpath', async({page, server}) => {
await page.setContent(`<div>some text</div>`);
const waitForXPath = page.waitForSelector('//html/body/div');
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
});

View file

@ -1,561 +0,0 @@
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const utils = require('./utils');
const {FFOX, CHROMIUM, WEBKIT, CHANNEL} = testOptions;
async function giveItTimeToLog(frame) {
await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
await frame.evaluate(() => new Promise(f => requestAnimationFrame(() => requestAnimationFrame(f))));
}
describe('Page.waitForTimeout', function() {
it('should timeout', async({page, server}) => {
const startTime = Date.now();
const timeout = 42;
await page.waitForTimeout(timeout);
expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
});
});
describe('Frame.waitForFunction', function() {
it('should accept a string', async({page, server}) => {
const watchdog = page.waitForFunction('window.__FOO === 1');
await page.evaluate(() => window.__FOO = 1);
await watchdog;
});
it('should work when resolved right before execution context disposal', async({page, server}) => {
await page.addInitScript(() => window.__RELOADED = true);
await page.waitForFunction(() => {
if (!window.__RELOADED)
window.location.reload();
return true;
});
});
it('should poll on interval', async({page, server}) => {
const polling = 100;
const timeDelta = await page.waitForFunction(() => {
if (!window.__startTime) {
window.__startTime = Date.now();
return false;
}
return Date.now() - window.__startTime;
}, {}, {polling});
expect(await timeDelta.jsonValue()).not.toBeLessThan(polling);
});
it('should avoid side effects after timeout', async({page, server}) => {
let counter = 0;
page.on('console', () => ++counter);
const error = await page.waitForFunction(() => {
window.counter = (window.counter || 0) + 1;
console.log(window.counter);
}, {}, { polling: 1, timeout: 1000 }).catch(e => e);
const savedCounter = counter;
await page.waitForTimeout(2000); // Give it some time to produce more logs.
expect(error.message).toContain('page.waitForFunction: Timeout 1000ms exceeded');
expect(counter).toBe(savedCounter);
});
it('should throw on polling:mutation', async({page, server}) => {
const error = await page.waitForFunction(() => true, {}, {polling: 'mutation'}).catch(e => e);
expect(error.message).toContain('Unknown polling option: mutation');
});
it('should poll on raf', async({page, server}) => {
const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'});
await page.evaluate(() => window.__FOO = 'hit');
await watchdog;
});
it('should fail with predicate throwing on first call', async({page, server}) => {
const error = await page.waitForFunction(() => { throw new Error('oh my'); }).catch(e => e);
expect(error.message).toContain('oh my');
});
it('should fail with predicate throwing sometimes', async({page, server}) => {
const error = await page.waitForFunction(() => {
window.counter = (window.counter || 0) + 1;
if (window.counter === 3)
throw new Error('Bad counter!');
return window.counter === 5 ? 'result' : false;
}).catch(e => e);
expect(error.message).toContain('Bad counter!');
});
it('should fail with ReferenceError on wrong page', async({page, server}) => {
const error = await page.waitForFunction(() => globalVar === 123).catch(e => e);
expect(error.message).toContain('globalVar');
});
it('should work with strict CSP policy', async({page, server}) => {
server.setCSP('/empty.html', 'script-src ' + server.PREFIX);
await page.goto(server.EMPTY_PAGE);
let error = null;
await Promise.all([
page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'raf'}).catch(e => error = e),
page.evaluate(() => window.__FOO = 'hit')
]);
expect(error).toBe(null);
});
it('should throw on bad polling value', async({page, server}) => {
let error = null;
try {
await page.waitForFunction(() => !!document.body, {}, {polling: 'unknown'});
} catch (e) {
error = e;
}
expect(error).toBeTruthy();
expect(error.message).toContain('polling');
});
it('should throw negative polling interval', async({page, server}) => {
let error = null;
try {
await page.waitForFunction(() => !!document.body, {}, {polling: -10});
} catch (e) {
error = e;
}
expect(error).toBeTruthy();
expect(error.message).toContain('Cannot poll with non-positive interval');
});
it('should return the success value as a JSHandle', async({page}) => {
expect(await (await page.waitForFunction(() => 5)).jsonValue()).toBe(5);
});
it('should return the window as a success value', async({ page }) => {
expect(await page.waitForFunction(() => window)).toBeTruthy();
});
it('should accept ElementHandle arguments', async({page}) => {
await page.setContent('<div></div>');
const div = await page.$('div');
let resolved = false;
const waitForFunction = page.waitForFunction(element => !element.parentElement, div).then(() => resolved = true);
expect(resolved).toBe(false);
await page.evaluate(element => element.remove(), div);
await waitForFunction;
});
it('should respect timeout', async({page, playwright}) => {
let error = null;
await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForFunction: Timeout 10ms exceeded');
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
});
it('should respect default timeout', async({page, playwright}) => {
page.setDefaultTimeout(1);
let error = null;
await page.waitForFunction('false').catch(e => error = e);
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
expect(error.message).toContain('page.waitForFunction: Timeout 1ms exceeded');
});
it('should disable timeout when its set to 0', async({page}) => {
const watchdog = page.waitForFunction(() => {
window.__counter = (window.__counter || 0) + 1;
return window.__injected;
}, {}, {timeout: 0, polling: 10});
await page.waitForFunction(() => window.__counter > 10);
await page.evaluate(() => window.__injected = true);
await watchdog;
});
it('should survive cross-process navigation', async({page, server}) => {
let fooFound = false;
const waitForFunction = page.waitForFunction('window.__FOO === 1').then(() => fooFound = true);
await page.goto(server.EMPTY_PAGE);
expect(fooFound).toBe(false);
await page.reload();
expect(fooFound).toBe(false);
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
expect(fooFound).toBe(false);
await page.evaluate(() => window.__FOO = 1);
await waitForFunction;
expect(fooFound).toBe(true);
});
it('should survive navigations', async({page, server}) => {
const watchdog = page.waitForFunction(() => window.__done);
await page.goto(server.EMPTY_PAGE);
await page.goto(server.PREFIX + '/consolelog.html');
await page.evaluate(() => window.__done = true);
await watchdog;
});
it('should work with multiline body', async({page, server}) => {
const result = await page.waitForFunction(`
(() => true)()
`);
expect(await result.jsonValue()).toBe(true);
});
it('should wait for predicate with arguments', async({page, server}) => {
await page.waitForFunction(({arg1, arg2}) => arg1 + arg2 === 3, { arg1: 1, arg2: 2});
});
});
describe('Frame.waitForSelector', function() {
const addElement = tag => document.body.appendChild(document.createElement(tag));
it('should throw on waitFor', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
let error;
await page.waitForSelector('*', { waitFor: 'attached' }).catch(e => error = e);
expect(error.message).toContain('options.waitFor is not supported, did you mean options.state?');
});
it('should tolerate waitFor=visible', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await page.waitForSelector('*', { waitFor: 'visible' }).catch(e => error = e);
});
it('should immediately resolve promise if node exists', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
await frame.waitForSelector('*');
await frame.evaluate(addElement, 'div');
await frame.waitForSelector('div', { state: 'attached'});
});
it('should work with removed MutationObserver', async({page, server}) => {
await page.evaluate(() => delete window.MutationObserver);
const [handle] = await Promise.all([
page.waitForSelector('.zombo'),
page.setContent(`<div class='zombo'>anything</div>`),
]);
expect(await page.evaluate(x => x.textContent, handle)).toBe('anything');
});
it('should resolve promise when node is added', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('div', { state: 'attached' });
await frame.evaluate(addElement, 'br');
await frame.evaluate(addElement, 'div');
const eHandle = await watchdog;
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
expect(tagName).toBe('DIV');
});
it('should report logs while waiting for visible', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('div', { timeout: 5000 });
await frame.evaluate(() => {
const div = document.createElement('div');
div.className = 'foo bar';
div.id = 'mydiv';
div.setAttribute('style', 'display: none');
div.setAttribute('foo', '123456789012345678901234567890123456789012345678901234567890');
div.textContent = 'abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvwyxz';
document.body.appendChild(div);
});
await giveItTimeToLog(frame);
await frame.evaluate(() => document.querySelector('div').remove());
await giveItTimeToLog(frame);
await frame.evaluate(() => {
const div = document.createElement('div');
div.className = 'another';
div.style.display = 'none';
document.body.appendChild(div);
});
await giveItTimeToLog(frame);
const error = await watchdog.catch(e => e);
expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`waiting for selector "div" to be visible`);
expect(error.message).toContain(`selector resolved to hidden <div id="mydiv" class="foo bar" foo="1234567890123456…>abcdefghijklmnopqrstuvwyxzabcdefghijklmnopqrstuvw…</div>`);
expect(error.message).toContain(`selector did not resolve to any element`);
expect(error.message).toContain(`selector resolved to hidden <div class="another"></div>`);
});
it('should report logs while waiting for hidden', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
await frame.evaluate(() => {
const div = document.createElement('div');
div.className = 'foo bar';
div.id = 'mydiv';
div.textContent = 'hello';
document.body.appendChild(div);
});
const watchdog = frame.waitForSelector('div', { state: 'hidden', timeout: 5000 });
await giveItTimeToLog(frame);
await frame.evaluate(() => {
document.querySelector('div').remove();
const div = document.createElement('div');
div.className = 'another';
div.textContent = 'hello';
document.body.appendChild(div);
});
await giveItTimeToLog(frame);
const error = await watchdog.catch(e => e);
expect(error.message).toContain(`frame.waitForSelector: Timeout 5000ms exceeded.`);
expect(error.message).toContain(`waiting for selector "div" to be hidden`);
expect(error.message).toContain(`selector resolved to visible <div id="mydiv" class="foo bar">hello</div>`);
expect(error.message).toContain(`selector resolved to visible <div class="another">hello</div>`);
});
it('should resolve promise when node is added in shadow dom', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const watchdog = page.waitForSelector('span');
await page.evaluate(() => {
const div = document.createElement('div');
div.attachShadow({mode: 'open'});
document.body.appendChild(div);
});
await page.evaluate(() => new Promise(f => setTimeout(f, 100)));
await page.evaluate(() => {
const span = document.createElement('span');
span.textContent = 'Hello from shadow';
document.querySelector('div').shadowRoot.appendChild(span);
});
const handle = await watchdog;
expect(await handle.evaluate(e => e.textContent)).toBe('Hello from shadow');
});
it('should work when node is added through innerHTML', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const watchdog = page.waitForSelector('h3 div', { state: 'attached'});
await page.evaluate(addElement, 'span');
await page.evaluate(() => document.querySelector('span').innerHTML = '<h3><div></div></h3>');
await watchdog;
});
it('Page.$ waitFor is shortcut for main frame', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const otherFrame = page.frames()[1];
const watchdog = page.waitForSelector('div', { state: 'attached' });
await otherFrame.evaluate(addElement, 'div');
await page.evaluate(addElement, 'div');
const eHandle = await watchdog;
expect(await eHandle.ownerFrame()).toBe(page.mainFrame());
});
it('should run in specified frame', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1];
const frame2 = page.frames()[2];
const waitForSelectorPromise = frame2.waitForSelector('div', { state: 'attached' });
await frame1.evaluate(addElement, 'div');
await frame2.evaluate(addElement, 'div');
const eHandle = await waitForSelectorPromise;
expect(await eHandle.ownerFrame()).toBe(frame2);
});
it('should throw when frame is detached', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1];
let waitError = null;
const waitPromise = frame.waitForSelector('.box').catch(e => waitError = e);
await utils.detachFrame(page, 'frame1');
await waitPromise;
expect(waitError).toBeTruthy();
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
});
it('should survive cross-process navigation', async({page, server}) => {
let boxFound = false;
const waitForSelector = page.waitForSelector('.box').then(() => boxFound = true);
await page.goto(server.EMPTY_PAGE);
expect(boxFound).toBe(false);
await page.reload();
expect(boxFound).toBe(false);
await page.goto(server.CROSS_PROCESS_PREFIX + '/grid.html');
await waitForSelector;
expect(boxFound).toBe(true);
});
it('should wait for visible', async({page, server}) => {
let divFound = false;
const waitForSelector = page.waitForSelector('div').then(() => divFound = true);
await page.setContent(`<div style='display: none; visibility: hidden;'>1</div>`);
expect(divFound).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
expect(divFound).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
expect(await waitForSelector).toBe(true);
expect(divFound).toBe(true);
});
it('should not consider visible when zero-sized', async({page, server}) => {
await page.setContent(`<div style='width: 0; height: 0;'>1</div>`);
let error = await page.waitForSelector('div', { timeout: 1000 }).catch(e => e);
expect(error.message).toContain('page.waitForSelector: Timeout 1000ms exceeded');
await page.evaluate(() => document.querySelector('div').style.width = '10px');
error = await page.waitForSelector('div', { timeout: 1000 }).catch(e => e);
expect(error.message).toContain('page.waitForSelector: Timeout 1000ms exceeded');
await page.evaluate(() => document.querySelector('div').style.height = '10px');
expect(await page.waitForSelector('div', { timeout: 1000 })).toBeTruthy();
});
it('should wait for visible recursively', async({page, server}) => {
let divVisible = false;
const waitForSelector = page.waitForSelector('div#inner').then(() => divVisible = true);
await page.setContent(`<div style='display: none; visibility: hidden;'><div id="inner">hi</div></div>`);
expect(divVisible).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('display'));
expect(divVisible).toBe(false);
await page.evaluate(() => document.querySelector('div').style.removeProperty('visibility'));
expect(await waitForSelector).toBe(true);
expect(divVisible).toBe(true);
});
it('hidden should wait for hidden', async({page, server}) => {
let divHidden = false;
await page.setContent(`<div style='display: block;'>content</div>`);
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divHidden = true);
await page.waitForSelector('div'); // do a round trip
expect(divHidden).toBe(false);
await page.evaluate(() => document.querySelector('div').style.setProperty('visibility', 'hidden'));
expect(await waitForSelector).toBe(true);
expect(divHidden).toBe(true);
});
it('hidden should wait for display: none', async({page, server}) => {
let divHidden = false;
await page.setContent(`<div style='display: block;'>content</div>`);
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divHidden = true);
await page.waitForSelector('div'); // do a round trip
expect(divHidden).toBe(false);
await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
expect(await waitForSelector).toBe(true);
expect(divHidden).toBe(true);
});
it('hidden should wait for removal', async({page, server}) => {
await page.setContent(`<div>content</div>`);
let divRemoved = false;
const waitForSelector = page.waitForSelector('div', { state: 'hidden' }).then(() => divRemoved = true);
await page.waitForSelector('div'); // do a round trip
expect(divRemoved).toBe(false);
await page.evaluate(() => document.querySelector('div').remove());
expect(await waitForSelector).toBe(true);
expect(divRemoved).toBe(true);
});
it('should return null if waiting to hide non-existing element', async({page, server}) => {
const handle = await page.waitForSelector('non-existing', { state: 'hidden' });
expect(handle).toBe(null);
});
it('should respect timeout', async({page, playwright}) => {
let error = null;
await page.waitForSelector('div', { timeout: 3000, state: 'attached' }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForSelector: Timeout 3000ms exceeded');
expect(error.message).toContain('waiting for selector "div"');
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
});
it('should have an error message specifically for awaiting an element to be hidden', async({page, server}) => {
await page.setContent(`<div>content</div>`);
let error = null;
await page.waitForSelector('div', { state: 'hidden', timeout: 1000 }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForSelector: Timeout 1000ms exceeded');
expect(error.message).toContain('waiting for selector "div" to be hidden');
});
it('should respond to node attribute mutation', async({page, server}) => {
let divFound = false;
const waitForSelector = page.waitForSelector('.zombo', { state: 'attached'}).then(() => divFound = true);
await page.setContent(`<div class='notZombo'></div>`);
expect(divFound).toBe(false);
await page.evaluate(() => document.querySelector('div').className = 'zombo');
expect(await waitForSelector).toBe(true);
});
it('should return the element handle', async({page, server}) => {
const waitForSelector = page.waitForSelector('.zombo');
await page.setContent(`<div class='zombo'>anything</div>`);
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
});
it('should have correct stack trace for timeout', async({page, server}) => {
let error;
await page.waitForSelector('.zombo', { timeout: 10 }).catch(e => error = e);
expect(error.stack).toContain('waittask');
});
it('should throw for unknown state option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { state: 'foo' }).catch(e => e);
expect(error.message).toContain('state: expected one of (attached|detached|visible|hidden)');
});
it('should throw for visibility option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
expect(error.message).toContain('options.visibility is not supported, did you mean options.state?');
});
it('should throw for true state option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { state: true }).catch(e => e);
expect(error.message).toContain('state: expected one of (attached|detached|visible|hidden)');
});
it('should throw for false state option', async({page, server}) => {
await page.setContent('<section>test</section>');
const error = await page.waitForSelector('section', { state: false }).catch(e => e);
expect(error.message).toContain('state: expected one of (attached|detached|visible|hidden)');
});
it('should support >> selector syntax', async({page, server}) => {
await page.goto(server.EMPTY_PAGE);
const frame = page.mainFrame();
const watchdog = frame.waitForSelector('css=div >> css=span', { state: 'attached'});
await frame.evaluate(addElement, 'br');
await frame.evaluate(addElement, 'div');
await frame.evaluate(() => document.querySelector('div').appendChild(document.createElement('span')));
const eHandle = await watchdog;
const tagName = await eHandle.getProperty('tagName').then(e => e.jsonValue());
expect(tagName).toBe('SPAN');
});
it('should wait for detached if already detached', async({page, server}) => {
await page.setContent('<section id="testAttribute">43543</section>');
expect(await page.waitForSelector('css=div', { state: 'detached'})).toBe(null);
});
it('should wait for detached', async({page, server}) => {
await page.setContent('<section id="testAttribute"><div>43543</div></section>');
let done = false;
const waitFor = page.waitForSelector('css=div', { state: 'detached'}).then(() => done = true);
expect(done).toBe(false);
await page.waitForSelector('css=section');
expect(done).toBe(false);
await page.$eval('div', div => div.remove());
expect(await waitFor).toBe(true);
expect(done).toBe(true);
});
});
describe('Frame.waitForSelector xpath', function() {
const addElement = tag => document.body.appendChild(document.createElement(tag));
it('should support some fancy xpath', async({page, server}) => {
await page.setContent(`<p>red herring</p><p>hello world </p>`);
const waitForXPath = page.waitForSelector('//p[normalize-space(.)="hello world"]');
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
});
it('should respect timeout', async({page, playwright}) => {
let error = null;
await page.waitForSelector('//div', { state: 'attached', timeout: 3000 }).catch(e => error = e);
expect(error).toBeTruthy();
expect(error.message).toContain('page.waitForSelector: Timeout 3000ms exceeded');
expect(error.message).toContain('waiting for selector "//div"');
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
});
it('should run in specified frame', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
await utils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
const frame1 = page.frames()[1];
const frame2 = page.frames()[2];
const waitForXPathPromise = frame2.waitForSelector('//div', { state: 'attached' });
await frame1.evaluate(addElement, 'div');
await frame2.evaluate(addElement, 'div');
const eHandle = await waitForXPathPromise;
expect(await eHandle.ownerFrame()).toBe(frame2);
});
it('should throw when frame is detached', async({page, server}) => {
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
const frame = page.frames()[1];
let waitError = null;
const waitPromise = frame.waitForSelector('//*[@class="box"]').catch(e => waitError = e);
await utils.detachFrame(page, 'frame1');
await waitPromise;
expect(waitError).toBeTruthy();
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
});
it('should return the element handle', async({page, server}) => {
const waitForXPath = page.waitForSelector('//*[@class="zombo"]');
await page.setContent(`<div class='zombo'>anything</div>`);
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
});
it('should allow you to select an element with single slash', async({page, server}) => {
await page.setContent(`<div>some text</div>`);
const waitForXPath = page.waitForSelector('//html/body/div');
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
});
});

View file

@ -1,142 +0,0 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
describe('Workers', function() {
it('Page.workers', async function({page, server}) {
await Promise.all([
page.waitForEvent('worker'),
page.goto(server.PREFIX + '/worker/worker.html')]);
const worker = page.workers()[0];
expect(worker.url()).toContain('worker.js');
expect(await worker.evaluate(() => self['workerFunction']())).toBe('worker function result');
await page.goto(server.EMPTY_PAGE);
expect(page.workers().length).toBe(0);
});
it('should emit created and destroyed events', async function({page}) {
const workerCreatedPromise = page.waitForEvent('worker');
const workerObj = await page.evaluateHandle(() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
const workerThisObj = await worker.evaluateHandle(() => this);
const workerDestroyedPromise = new Promise(x => worker.once('close', x));
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
expect(await workerDestroyedPromise).toBe(worker);
const error = await workerThisObj.getProperty('self').catch(error => error);
expect(error.message).toContain('Most likely the worker has been closed.');
});
it('should report console logs', async function({page}) {
const [message] = await Promise.all([
page.waitForEvent('console'),
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
]);
expect(message.text()).toBe('1');
});
it('should have JSHandles for console logs', async function({page}) {
const logPromise = new Promise(x => page.on('console', x));
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1,2,3,this)'], {type: 'application/javascript'}))));
const log = await logPromise;
expect(log.text()).toBe('1 2 3 JSHandle@object');
expect(log.args().length).toBe(4);
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
});
it('should evaluate', async function({page}) {
const workerCreatedPromise = page.waitForEvent('worker');
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
expect(await worker.evaluate('1+1')).toBe(2);
});
it('should report errors', async function({page}) {
const errorPromise = new Promise(x => page.on('pageerror', x));
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob([`
setTimeout(() => {
// Do a console.log just to check that we do not confuse it with an error.
console.log('hey');
throw new Error('this is my error');
})
`], {type: 'application/javascript'}))));
const errorLog = await errorPromise;
expect(errorLog.message).toContain('this is my error');
});
it('should clear upon navigation', async function({server, page}) {
await page.goto(server.EMPTY_PAGE);
const workerCreatedPromise = page.waitForEvent('worker');
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
expect(page.workers().length).toBe(1);
let destroyed = false;
worker.once('close', () => destroyed = true);
await page.goto(server.PREFIX + '/one-style.html');
expect(destroyed).toBe(true);
expect(page.workers().length).toBe(0);
});
it('should clear upon cross-process navigation', async function({server, page}) {
await page.goto(server.EMPTY_PAGE);
const workerCreatedPromise = page.waitForEvent('worker');
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
expect(page.workers().length).toBe(1);
let destroyed = false;
worker.once('close', () => destroyed = true);
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(destroyed).toBe(true);
expect(page.workers().length).toBe(0);
});
it('should report network activity', async function({page, server}) {
const [worker] = await Promise.all([
page.waitForEvent('worker'),
page.goto(server.PREFIX + '/worker/worker.html'),
]);
const url = server.PREFIX + '/one-style.css';
const requestPromise = page.waitForRequest(url);
const responsePromise = page.waitForResponse(url);
await worker.evaluate(url => fetch(url).then(response => response.text()).then(console.log), url);
const request = await requestPromise;
const response = await responsePromise;
expect(request.url()).toBe(url);
expect(response.request()).toBe(request);
expect(response.ok()).toBe(true);
});
it('should report network activity on worker creation', async function({page, server}) {
// Chromium needs waitForDebugger enabled for this one.
await page.goto(server.EMPTY_PAGE);
const url = server.PREFIX + '/one-style.css';
const requestPromise = page.waitForRequest(url);
const responsePromise = page.waitForResponse(url);
await page.evaluate(url => new Worker(URL.createObjectURL(new Blob([`
fetch("${url}").then(response => response.text()).then(console.log);
`], {type: 'application/javascript'}))), url);
const request = await requestPromise;
const response = await responsePromise;
expect(request.url()).toBe(url);
expect(response.request()).toBe(request);
expect(response.ok()).toBe(true);
});
it('should format number using context locale', async({browser, server}) => {
const context = await browser.newContext({ locale: 'ru-RU' });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
const [worker] = await Promise.all([
page.waitForEvent('worker'),
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
]);
expect(await worker.evaluate(() => (10000.20).toLocaleString())).toBe('10\u00A0000,2');
await context.close();
});
});

140
test/workers.spec.js Normal file
View file

@ -0,0 +1,140 @@
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const {FFOX, CHROMIUM, WEBKIT} = testOptions;
it('Page.workers', async function({page, server}) {
await Promise.all([
page.waitForEvent('worker'),
page.goto(server.PREFIX + '/worker/worker.html')]);
const worker = page.workers()[0];
expect(worker.url()).toContain('worker.js');
expect(await worker.evaluate(() => self['workerFunction']())).toBe('worker function result');
await page.goto(server.EMPTY_PAGE);
expect(page.workers().length).toBe(0);
});
it('should emit created and destroyed events', async function({page}) {
const workerCreatedPromise = page.waitForEvent('worker');
const workerObj = await page.evaluateHandle(() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
const workerThisObj = await worker.evaluateHandle(() => this);
const workerDestroyedPromise = new Promise(x => worker.once('close', x));
await page.evaluate(workerObj => workerObj.terminate(), workerObj);
expect(await workerDestroyedPromise).toBe(worker);
const error = await workerThisObj.getProperty('self').catch(error => error);
expect(error.message).toContain('Most likely the worker has been closed.');
});
it('should report console logs', async function({page}) {
const [message] = await Promise.all([
page.waitForEvent('console'),
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
]);
expect(message.text()).toBe('1');
});
it('should have JSHandles for console logs', async function({page}) {
const logPromise = new Promise(x => page.on('console', x));
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1,2,3,this)'], {type: 'application/javascript'}))));
const log = await logPromise;
expect(log.text()).toBe('1 2 3 JSHandle@object');
expect(log.args().length).toBe(4);
expect(await (await log.args()[3].getProperty('origin')).jsonValue()).toBe('null');
});
it('should evaluate', async function({page}) {
const workerCreatedPromise = page.waitForEvent('worker');
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
expect(await worker.evaluate('1+1')).toBe(2);
});
it('should report errors', async function({page}) {
const errorPromise = new Promise(x => page.on('pageerror', x));
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob([`
setTimeout(() => {
// Do a console.log just to check that we do not confuse it with an error.
console.log('hey');
throw new Error('this is my error');
})
`], {type: 'application/javascript'}))));
const errorLog = await errorPromise;
expect(errorLog.message).toContain('this is my error');
});
it('should clear upon navigation', async function({server, page}) {
await page.goto(server.EMPTY_PAGE);
const workerCreatedPromise = page.waitForEvent('worker');
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
expect(page.workers().length).toBe(1);
let destroyed = false;
worker.once('close', () => destroyed = true);
await page.goto(server.PREFIX + '/one-style.html');
expect(destroyed).toBe(true);
expect(page.workers().length).toBe(0);
});
it('should clear upon cross-process navigation', async function({server, page}) {
await page.goto(server.EMPTY_PAGE);
const workerCreatedPromise = page.waitForEvent('worker');
await page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'}))));
const worker = await workerCreatedPromise;
expect(page.workers().length).toBe(1);
let destroyed = false;
worker.once('close', () => destroyed = true);
await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html');
expect(destroyed).toBe(true);
expect(page.workers().length).toBe(0);
});
it('should report network activity', async function({page, server}) {
const [worker] = await Promise.all([
page.waitForEvent('worker'),
page.goto(server.PREFIX + '/worker/worker.html'),
]);
const url = server.PREFIX + '/one-style.css';
const requestPromise = page.waitForRequest(url);
const responsePromise = page.waitForResponse(url);
await worker.evaluate(url => fetch(url).then(response => response.text()).then(console.log), url);
const request = await requestPromise;
const response = await responsePromise;
expect(request.url()).toBe(url);
expect(response.request()).toBe(request);
expect(response.ok()).toBe(true);
});
it('should report network activity on worker creation', async function({page, server}) {
// Chromium needs waitForDebugger enabled for this one.
await page.goto(server.EMPTY_PAGE);
const url = server.PREFIX + '/one-style.css';
const requestPromise = page.waitForRequest(url);
const responsePromise = page.waitForResponse(url);
await page.evaluate(url => new Worker(URL.createObjectURL(new Blob([`
fetch("${url}").then(response => response.text()).then(console.log);
`], {type: 'application/javascript'}))), url);
const request = await requestPromise;
const response = await responsePromise;
expect(request.url()).toBe(url);
expect(response.request()).toBe(request);
expect(response.ok()).toBe(true);
});
it('should format number using context locale', async({browser, server}) => {
const context = await browser.newContext({ locale: 'ru-RU' });
const page = await context.newPage();
await page.goto(server.EMPTY_PAGE);
const [worker] = await Promise.all([
page.waitForEvent('worker'),
page.evaluate(() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))),
]);
expect(await worker.evaluate(() => (10000.20).toLocaleString())).toBe('10\u00A0000,2');
await context.close();
});