test: remove describes (3) (#3278)
This commit is contained in:
parent
de55fa6482
commit
573f580fa8
348
test/elementhandle-screenshot.spec.js
Normal file
348
test/elementhandle-screenshot.spec.js
Normal 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);"> </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);
|
||||
});
|
||||
46
test/jshandle-as-element.spec.js
Normal file
46
test/jshandle-as-element.spec.js
Normal 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);
|
||||
});
|
||||
37
test/jshandle-json-value.spec.js
Normal file
37
test/jshandle-json-value.spec.js
Normal 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');
|
||||
});
|
||||
94
test/jshandle-properties.spec.js
Normal file
94
test/jshandle-properties.spec.js
Normal 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');
|
||||
});
|
||||
59
test/jshandle-to-string.spec.js
Normal file
59
test/jshandle-to-string.spec.js
Normal 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');
|
||||
});
|
||||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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
104
test/multiclient.spec.js
Normal 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();
|
||||
});
|
||||
192
test/network-request.spec.js
Normal file
192
test/network-request.spec.js
Normal 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);
|
||||
});
|
||||
118
test/network-response.spec.js
Normal file
118
test/network-response.spec.js
Normal 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!');
|
||||
});
|
||||
|
|
@ -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.');
|
||||
});
|
||||
});
|
||||
109
test/page-evaluate-handle.spec.js
Normal file
109
test/page-evaluate-handle.spec.js
Normal 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);
|
||||
});
|
||||
119
test/page-event-network.spec.js
Normal file
119
test/page-event-network.spec.js
Normal 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());
|
||||
});
|
||||
58
test/page-event-request.spec.js
Normal file
58
test/page-event-request.spec.js
Normal 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');
|
||||
});
|
||||
249
test/page-screenshot.spec.js
Normal file
249
test/page-screenshot.spec.js
Normal 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');
|
||||
});
|
||||
86
test/page-set-extra-http-headers.spec.js
Normal file
86
test/page-set-extra-http-headers.spec.js
Normal 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.');
|
||||
});
|
||||
|
|
@ -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);"> </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);
|
||||
});
|
||||
});
|
||||
210
test/wait-for-function.spec.js
Normal file
210
test/wait-for-function.spec.js
Normal 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});
|
||||
});
|
||||
417
test/wait-for-selector.spec.js
Normal file
417
test/wait-for-selector.spec.js
Normal 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');
|
||||
});
|
||||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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
140
test/workers.spec.js
Normal 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();
|
||||
});
|
||||
Loading…
Reference in a new issue