ya
-
hello
-
hello
-
hello
-
-
-
`);
- expect(await playwright.selectors._createSelector('z', await page.$('#target'))).toBe('"ya"~"hey"~"hello"');
- expect(await page.$eval(`z="ya"~"hey"~"hello"`, e => e.outerHTML)).toBe('
hello
');
- expect(await page.$eval(`z="ya"~"hey"~"unique"`, e => e.outerHTML).catch(e => e.message)).toBe('Error: failed to find element matching selector "z="ya"~"hey"~"unique""');
- expect(await page.$$eval(`z="ya" ~ "hey" ~ "hello"`, es => es.map(e => e.outerHTML).join('\n'))).toBe('
hello
\n
hello
');
- });
-
- it('should query existing element with zs selector', async({page, server}) => {
- await page.goto(server.PREFIX + '/playground.html');
- await page.setContent('
');
- const html = await page.$('z=html');
- const second = await html.$('z=.second');
- const inner = await second.$('z=.inner');
- const content = await page.evaluate(e => e.textContent, inner);
- expect(content).toBe('A');
- });
+ error = await playwright.selectors.register('css', createDummySelector).catch(e => e);
+ expect(error.message).toBe('"css" is a predefined selector engine');
});
-
- describe('text selector', () => {
- it('query', async ({page}) => {
- await page.setContent(`
yo
ya
\nye
`);
- expect(await page.$eval(`text=ya`, e => e.outerHTML)).toBe('
ya
');
- expect(await page.$eval(`text="ya"`, e => e.outerHTML)).toBe('
ya
');
- expect(await page.$eval(`text=/^[ay]+$/`, e => e.outerHTML)).toBe('
ya
');
- expect(await page.$eval(`text=/Ya/i`, e => e.outerHTML)).toBe('
ya
');
- expect(await page.$eval(`text=ye`, e => e.outerHTML)).toBe('
\nye
');
-
- await page.setContent(`
ye
ye
`);
- expect(await page.$eval(`text="ye"`, e => e.outerHTML)).toBe('
ye
');
-
- await page.setContent(`
yo
"ya
hello world!
`);
- expect(await page.$eval(`text="\\"ya"`, e => e.outerHTML)).toBe('
"ya
');
- expect(await page.$eval(`text=/hello/`, e => e.outerHTML)).toBe('
hello world!
');
- expect(await page.$eval(`text=/^\\s*heLLo/i`, e => e.outerHTML)).toBe('
hello world!
');
-
- await page.setContent(`
`);
- expect(await page.$eval(`text=hey`, e => e.outerHTML)).toBe('
');
-
- await page.setContent(`
yo
yo
`);
- expect(await page.$$eval(`text=yo`, es => es.map(e => e.outerHTML).join('\n'))).toBe('
yo
\n
yo
');
- });
-
- it('create', async ({page}) => {
- await page.setContent(`
yo
"ya
ye ye
`);
- expect(await playwright.selectors._createSelector('text', await page.$('div'))).toBe('yo');
- expect(await playwright.selectors._createSelector('text', await page.$('div:nth-child(2)'))).toBe('"\\"ya"');
- expect(await playwright.selectors._createSelector('text', await page.$('div:nth-child(3)'))).toBe('"ye ye"');
-
- await page.setContent(`
yo
`);
- expect(await playwright.selectors._createSelector('text', await page.$('div:nth-child(2)'))).toBe('hey');
-
- await page.setContent(`
`);
- expect(await playwright.selectors._createSelector('text', await page.$('div'))).toBe('yo');
-
- await page.setContent(`
`);
- expect(await playwright.selectors._createSelector('text', await page.$('div'))).toBe('" \\"yo "');
- });
-
- it('should be case sensitive if quotes are specified', async({page}) => {
- await page.setContent(`
yo
ya
\nye
`);
- expect(await page.$eval(`text=yA`, e => e.outerHTML)).toBe('
ya
');
- expect(await page.$(`text="yA"`)).toBe(null);
- });
-
- it('should search for a substring without quotes', async({page}) => {
- await page.setContent(`
textwithsubstring
`);
- expect(await page.$eval(`text=with`, e => e.outerHTML)).toBe('
textwithsubstring
');
- expect(await page.$(`text="with"`)).toBe(null);
- });
-
- it('should match input[type=button|submit]', async({page}) => {
- await page.setContent(`
`);
- expect(await page.$eval(`text=hello`, e => e.outerHTML)).toBe('
');
- expect(await page.$eval(`text=world`, e => e.outerHTML)).toBe('
');
- });
-
- it('should work for open shadow roots', async({page, server}) => {
- await page.goto(server.PREFIX + '/deep-shadow.html');
- expect(await page.$eval(`text=root1`, e => e.textContent)).toBe('Hello from root1');
- expect(await page.$eval(`text=root2`, e => e.textContent)).toBe('Hello from root2');
- expect(await page.$eval(`text=root3`, e => e.textContent)).toBe('Hello from root3');
- });
- });
-
- describe('attribute selector', () => {
- it('should work for open shadow roots', async({page, server}) => {
- await page.goto(server.PREFIX + '/deep-shadow.html');
- expect(await page.$eval(`id=target`, e => e.textContent)).toBe('Hello from root2');
- expect(await page.$eval(`data-testid=foo`, e => e.textContent)).toBe('Hello from root1');
- expect(await page.$$eval(`data-testid=foo`, els => els.length)).toBe(3);
- });
- });
-
- describe('selectors.register', () => {
- it('should work', async ({page}) => {
- const createTagSelector = () => ({
- create(root, target) {
- return target.nodeName;
- },
- query(root, selector) {
- return root.querySelector(selector);
- },
- queryAll(root, selector) {
- return Array.from(root.querySelectorAll(selector));
- }
- });
- await playwright.selectors.register('tag', `(${createTagSelector.toString()})()`);
- await page.setContent('
');
- expect(await playwright.selectors._createSelector('tag', await page.$('div'))).toBe('DIV');
- expect(await page.$eval('tag=DIV', e => e.nodeName)).toBe('DIV');
- expect(await page.$eval('tag=SPAN', e => e.nodeName)).toBe('SPAN');
- expect(await page.$$eval('tag=DIV', es => es.length)).toBe(2);
- });
- it('should work with path', async ({page}) => {
- await playwright.selectors.register('foo', { path: path.join(__dirname, 'assets/sectionselectorengine.js') });
- await page.setContent('
');
- expect(await page.$eval('foo=whatever', e => e.nodeName)).toBe('SECTION');
- });
- it('should work in main and isolated world', async ({page}) => {
- const createDummySelector = () => ({
- create(root, target) { },
- query(root, selector) {
- return window.__answer;
- },
- queryAll(root, selector) {
- return [document.body, document.documentElement, window.__answer];
- }
- });
- await playwright.selectors.register('main', createDummySelector);
- await playwright.selectors.register('isolated', createDummySelector, { contentScript: true });
- await page.setContent('
');
- await page.evaluate(() => window.__answer = document.querySelector('span'));
- // Works in main if asked.
- expect(await page.$eval('main=ignored', e => e.nodeName)).toBe('SPAN');
- expect(await page.$eval('css=div >> main=ignored', e => e.nodeName)).toBe('SPAN');
- expect(await page.$$eval('main=ignored', es => window.__answer !== undefined)).toBe(true);
- expect(await page.$$eval('main=ignored', es => es.filter(e => e).length)).toBe(3);
- // Works in isolated by default.
- expect(await page.$('isolated=ignored')).toBe(null);
- expect(await page.$('css=div >> isolated=ignored')).toBe(null);
- // $$eval always works in main, to avoid adopting nodes one by one.
- expect(await page.$$eval('isolated=ignored', es => window.__answer !== undefined)).toBe(true);
- expect(await page.$$eval('isolated=ignored', es => es.filter(e => e).length)).toBe(3);
- // At least one engine in main forces all to be in main.
- expect(await page.$eval('main=ignored >> isolated=ignored', e => e.nodeName)).toBe('SPAN');
- expect(await page.$eval('isolated=ignored >> main=ignored', e => e.nodeName)).toBe('SPAN');
- // Can be chained to css.
- expect(await page.$eval('main=ignored >> css=section', e => e.nodeName)).toBe('SECTION');
- });
- it('should update', async ({page}) => {
- await page.setContent('
');
- expect(await page.$eval('div', e => e.nodeName)).toBe('DIV');
-
- let error = await page.$('dummy=ignored').catch(e => e);
- expect(error.message).toContain('Unknown engine dummy while parsing selector dummy=ignored');
-
- const createDummySelector = () => ({
- create(root, target) {
- return target.nodeName;
- },
- query(root, selector) {
- return root.querySelector('dummy');
- },
- queryAll(root, selector) {
- return Array.from(root.querySelectorAll('dummy'));
- }
- });
-
- error = await playwright.selectors.register('$', createDummySelector).catch(e => e);
- expect(error.message).toBe('Selector engine name may only contain [a-zA-Z0-9_] characters');
-
- await playwright.selectors.register('dummy', createDummySelector);
- expect(await page.$eval('dummy=ignored', e => e.id)).toBe('d1');
- expect(await page.$eval('css=span >> dummy=ignored', e => e.id)).toBe('d2');
-
- error = await playwright.selectors.register('dummy', createDummySelector).catch(e => e);
- expect(error.message).toBe('"dummy" selector engine has been already registered');
-
- error = await playwright.selectors.register('css', createDummySelector).catch(e => e);
- expect(error.message).toBe('"css" is a predefined selector engine');
- });
- });
-};
+});
diff --git a/test/screenshot.spec.js b/test/screenshot.spec.js
index cd73e49b56..c2d0bc0bdd 100644
--- a/test/screenshot.spec.js
+++ b/test/screenshot.spec.js
@@ -15,469 +15,465 @@
* limitations under the License.
*/
-/**
- * @type {PageTestSuite}
- */
-module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, LINUX }) {
+const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
- describe('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).toBe('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
- }
- }));
+describe('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
}
- 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');
+ 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,
+ },
});
- 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);
- expect(page.viewportSize().width).toBe(500);
- expect(page.viewportSize().height).toBe(500);
+ 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
+ }
});
- it('should run in parallel in multiple pages', async({page, server, context}) => {
- const N = 2;
- 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, 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}.png`);
- await Promise.all(pages.map(page => page.close()));
- });
- it.fail(FFOX)('should allow transparency', async({page, server}) => {
- await page.setViewportSize({ width: 50, height: 150 });
- await page.setContent(`
-
-
-
-
- `);
- 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, server}) => {
- const screenshot = await page.screenshot({
+ 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).toBe('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: 0,
+ x: 50 * i,
y: 0,
- width: 11,
- height: 11,
+ width: 50,
+ height: 50
}
- });
- expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
+ }));
+ }
+ 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
});
- it.skip(FFOX)('should work with a mobile viewport', async({browser, server}) => {
- const context = await browser.newContext({ viewport: { width: 320, height: 480 }, isMobile: 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);
+ expect(page.viewportSize().width).toBe(500);
+ expect(page.viewportSize().height).toBe(500);
+ });
+ it('should run in parallel in multiple pages', async({page, server, context}) => {
+ const N = 2;
+ const pages = await Promise.all(Array(N).fill(0).map(async() => {
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);
+ 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, 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}.png`);
+ await Promise.all(pages.map(page => page.close()));
+ });
+ it.fail(FFOX)('should allow transparency', async({page, server}) => {
+ await page.setViewportSize({ width: 50, height: 150 });
+ await page.setContent(`
+
+
+
+
+ `);
+ 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, server}) => {
+ const screenshot = await page.screenshot({
+ clip: {
+ x: 0,
+ y: 0,
+ width: 11,
+ height: 11,
}
});
- 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();
- });
+ expect(screenshot).toBeGolden('screenshot-clip-odd-size.png');
});
-
- describe('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, server}) => {
- await page.setViewportSize({width: 500, height: 500});
- await page.setContent(`
-
oooo
-
-
- `);
- 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, server}) => {
- await page.setViewportSize({width: 500, height: 500});
-
- await page.setContent(`
-
oooo
-
-
-
-
- `);
- const elementHandles = await page.$$('div.to-screenshot');
- const promises = elementHandles.map(handle => handle.screenshot());
- const screenshots = await Promise.all(promises);
- expect(screenshots[2]).toBeGolden('screenshot-element-larger-than-viewport.png');
-
- expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
- });
- it('should capture full element when larger than viewport', async({page, server}) => {
- await page.setViewportSize({width: 500, height: 500});
-
- await page.setContent(`
-
oooo
-
-
-
-
- `);
- const elementHandle = await page.$('div.to-screenshot');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');
-
- expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
- });
- it('should scroll element into view', async({page, server}) => {
- await page.setViewportSize({width: 500, height: 500});
- await page.setContent(`
-
oooo
-
-
-
- `);
- const elementHandle = await page.$('div.to-screenshot');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
- });
- it('should scroll 15000px into view', async({page, server}) => {
- await page.setViewportSize({width: 500, height: 500});
- await page.setContent(`
-
oooo
-
-
-
- `);
- const elementHandle = await page.$('div.to-screenshot');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
- });
- it('should work with a rotated element', async({page, server}) => {
- await page.setViewportSize({width: 500, height: 500});
- await page.setContent(`
`);
- 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('
remove this ');
- const elementHandle = await page.$('h1');
- await page.evaluate(element => element.remove(), elementHandle);
- const screenshotError = await elementHandle.screenshot().catch(error => error);
- expect(screenshotError.message).toContain('Node is detached');
- });
- it('should not hang with zero width/height element', async({page, server}) => {
- await page.setContent('
');
- const div = await page.$('div');
- const error = await div.screenshot().catch(e => e);
- expect(error.message).toBe('Node has 0 height.');
- });
- it('should work for an element with fractional dimensions', async({page}) => {
- await page.setContent('
');
- 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('
');
- 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.goto(server.PREFIX + '/grid.html');
- const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
- const screenshot = await page.screenshot();
- expect(screenshot).toBeInstanceOf(Buffer);
-
- const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
- expect(sizeBefore.width).toBe(sizeAfter.width);
- expect(sizeBefore.height).toBe(sizeAfter.height);
- await context.close();
- });
- it('should take fullPage screenshots when default viewport is null', async({server, browser}) => {
- const context = await browser.newContext({ viewport: null });
- const page = await context.newPage();
- await page.goto(server.PREFIX + '/grid.html');
- const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
- const screenshot = await page.screenshot({
- fullPage: true
+ it.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);
-
- 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();
- expect(page.viewportSize().width).toBe(456);
- expect(page.viewportSize().height).toBe(789);
- expect(await page.evaluate('window.innerWidth')).toBe(456);
- expect(await page.evaluate('window.innerHeight')).toBe(789);
- const screenshot = await page.screenshot({ fullPage: true });
- expect(screenshot).toBeInstanceOf(Buffer);
- expect(page.viewportSize().width).toBe(456);
- expect(page.viewportSize().height).toBe(789);
- expect(await page.evaluate('window.innerWidth')).toBe(456);
- expect(await page.evaluate('window.innerHeight')).toBe(789);
- 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(`
-
oooo
-
-
-
-
- `);
- const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
- const elementHandle = await page.$('div.to-screenshot');
- const screenshot = await elementHandle.screenshot();
- expect(screenshot).toBeInstanceOf(Buffer);
- const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
- expect(sizeBefore.width).toBe(sizeAfter.width);
- expect(sizeBefore.height).toBe(sizeAfter.height);
- await context.close();
- });
+ }
});
-};
+ it('should 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();
+ });
+});
+
+describe('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, server}) => {
+ await page.setViewportSize({width: 500, height: 500});
+ await page.setContent(`
+
oooo
+
+
+ `);
+ 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, server}) => {
+ await page.setViewportSize({width: 500, height: 500});
+
+ await page.setContent(`
+
oooo
+
+
+
+
+ `);
+ const elementHandles = await page.$$('div.to-screenshot');
+ const promises = elementHandles.map(handle => handle.screenshot());
+ const screenshots = await Promise.all(promises);
+ expect(screenshots[2]).toBeGolden('screenshot-element-larger-than-viewport.png');
+
+ expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
+ });
+ it('should capture full element when larger than viewport', async({page, server}) => {
+ await page.setViewportSize({width: 500, height: 500});
+
+ await page.setContent(`
+
oooo
+
+
+
+
+ `);
+ const elementHandle = await page.$('div.to-screenshot');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-larger-than-viewport.png');
+
+ expect(await page.evaluate(() => ({ w: window.innerWidth, h: window.innerHeight }))).toEqual({ w: 500, h: 500 });
+ });
+ it('should scroll element into view', async({page, server}) => {
+ await page.setViewportSize({width: 500, height: 500});
+ await page.setContent(`
+
oooo
+
+
+
+ `);
+ const elementHandle = await page.$('div.to-screenshot');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
+ });
+ it('should scroll 15000px into view', async({page, server}) => {
+ await page.setViewportSize({width: 500, height: 500});
+ await page.setContent(`
+
oooo
+
+
+
+ `);
+ const elementHandle = await page.$('div.to-screenshot');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeGolden('screenshot-element-scrolled-into-view.png');
+ });
+ it('should work with a rotated element', async({page, server}) => {
+ await page.setViewportSize({width: 500, height: 500});
+ await page.setContent(`
`);
+ 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('
remove this ');
+ const elementHandle = await page.$('h1');
+ await page.evaluate(element => element.remove(), elementHandle);
+ const screenshotError = await elementHandle.screenshot().catch(error => error);
+ expect(screenshotError.message).toContain('Node is detached');
+ });
+ it('should not hang with zero width/height element', async({page, server}) => {
+ await page.setContent('
');
+ const div = await page.$('div');
+ const error = await div.screenshot().catch(e => e);
+ expect(error.message).toBe('Node has 0 height.');
+ });
+ it('should work for an element with fractional dimensions', async({page}) => {
+ await page.setContent('
');
+ 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('
');
+ 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.goto(server.PREFIX + '/grid.html');
+ const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
+ const screenshot = await page.screenshot();
+ expect(screenshot).toBeInstanceOf(Buffer);
+
+ const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
+ expect(sizeBefore.width).toBe(sizeAfter.width);
+ expect(sizeBefore.height).toBe(sizeAfter.height);
+ await context.close();
+ });
+ it('should 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();
+ expect(page.viewportSize().width).toBe(456);
+ expect(page.viewportSize().height).toBe(789);
+ expect(await page.evaluate('window.innerWidth')).toBe(456);
+ expect(await page.evaluate('window.innerHeight')).toBe(789);
+ const screenshot = await page.screenshot({ fullPage: true });
+ expect(screenshot).toBeInstanceOf(Buffer);
+ expect(page.viewportSize().width).toBe(456);
+ expect(page.viewportSize().height).toBe(789);
+ expect(await page.evaluate('window.innerWidth')).toBe(456);
+ expect(await page.evaluate('window.innerHeight')).toBe(789);
+ 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(`
+
oooo
+
+
+
+
+ `);
+ const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
+ const elementHandle = await page.$('div.to-screenshot');
+ const screenshot = await elementHandle.screenshot();
+ expect(screenshot).toBeInstanceOf(Buffer);
+ const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
+ expect(sizeBefore.width).toBe(sizeAfter.width);
+ expect(sizeBefore.height).toBe(sizeAfter.height);
+ await context.close();
+ });
+});
diff --git a/test/waittask.spec.js b/test/waittask.spec.js
index f4aede4683..0bc0a76b43 100644
--- a/test/waittask.spec.js
+++ b/test/waittask.spec.js
@@ -16,452 +16,446 @@
*/
const utils = require('./utils');
+const {FFOX, CHROMIUM, WEBKIT} = utils.testOptions(browserType);
-/**
- * @type {PageTestSuite}
- */
-module.exports.describe = function({playwright, FFOX, CHROMIUM, WEBKIT}) {
+describe('Page.waitFor', function() {
+ it('should wait for selector', async({page, server}) => {
+ let found = false;
+ const waitFor = page.waitFor('div').then(() => found = true);
+ await page.goto(server.EMPTY_PAGE);
+ expect(found).toBe(false);
+ await page.goto(server.PREFIX + '/grid.html');
+ await waitFor;
+ expect(found).toBe(true);
+ });
+ it('should wait for an xpath', async({page, server}) => {
+ let found = false;
+ const waitFor = page.waitFor('//div').then(() => found = true);
+ await page.goto(server.EMPTY_PAGE);
+ expect(found).toBe(false);
+ await page.goto(server.PREFIX + '/grid.html');
+ await waitFor;
+ expect(found).toBe(true);
+ });
+ it('should not allow you to select an element with single slash xpath', async({page, server}) => {
+ await page.setContent(`
some text
`);
+ let error = null;
+ await page.waitFor('/html/body/div').catch(e => error = e);
+ expect(error).toBeTruthy();
+ });
+ it('should timeout', async({page, server}) => {
+ const startTime = Date.now();
+ const timeout = 42;
+ await page.waitFor(timeout);
+ expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
+ });
+ 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', async({page, server}) => {
+ await Promise.all([
+ page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide
+ page.setViewportSize({width: 10, height: 10}),
+ ]);
+ });
+ it('should throw when unknown type', async({page, server}) => {
+ let error = null;
+ await page.waitFor({foo: 'bar'}).catch(e => error = e);
+ expect(error.message).toContain('Unsupported target type');
+ });
+ it('should wait for predicate with arguments', async({page, server}) => {
+ await page.waitFor(({arg1, arg2}) => arg1 + arg2 === 3, {}, { arg1: 1, arg2: 2});
+ });
+});
- describe('Page.waitFor', function() {
- it('should wait for selector', async({page, server}) => {
- let found = false;
- const waitFor = page.waitFor('div').then(() => found = true);
- await page.goto(server.EMPTY_PAGE);
- expect(found).toBe(false);
- await page.goto(server.PREFIX + '/grid.html');
- await waitFor;
- expect(found).toBe(true);
- });
- it('should wait for an xpath', async({page, server}) => {
- let found = false;
- const waitFor = page.waitFor('//div').then(() => found = true);
- await page.goto(server.EMPTY_PAGE);
- expect(found).toBe(false);
- await page.goto(server.PREFIX + '/grid.html');
- await waitFor;
- expect(found).toBe(true);
- });
- it('should not allow you to select an element with single slash xpath', async({page, server}) => {
- await page.setContent(`
some text
`);
- let error = null;
- await page.waitFor('/html/body/div').catch(e => error = e);
- expect(error).toBeTruthy();
- });
- it('should timeout', async({page, server}) => {
- const startTime = Date.now();
- const timeout = 42;
- await page.waitFor(timeout);
- expect(Date.now() - startTime).not.toBeLessThan(timeout / 2);
- });
- 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', async({page, server}) => {
- await Promise.all([
- page.waitFor(() => window.innerWidth < 130), // Windows doesn't like windows below 120px wide
- page.setViewportSize({width: 10, height: 10}),
- ]);
- });
- it('should throw when unknown type', async({page, server}) => {
- let error = null;
- await page.waitFor({foo: 'bar'}).catch(e => error = e);
- expect(error.message).toContain('Unsupported target type');
- });
- it('should wait for predicate with arguments', async({page, server}) => {
- await page.waitFor(({arg1, arg2}) => arg1 + arg2 === 3, {}, { arg1: 1, arg2: 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;
});
});
-
- 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(timeDelta).not.toBeLessThan(polling);
- });
- it('should poll on mutation', async({page, server}) => {
- let success = false;
- const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'mutation'})
- .then(() => success = true);
- await page.evaluate(() => window.__FOO = 'hit');
- expect(success).toBe(false);
- await page.evaluate(() => document.body.appendChild(document.createElement('div')));
- await watchdog;
- });
- 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 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;
+ 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;
}
- 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('
');
- 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}) => {
- let error = null;
- await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting for function failed: timeout');
- expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
- });
- it('should respect default timeout', async({page}) => {
- page.setDefaultTimeout(1);
- let error = null;
- await page.waitForFunction('false').catch(e => error = e);
- expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
- expect(error.message).toContain('waiting for function failed: timeout');
- });
- 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;
- });
+ return Date.now() - window.__startTime;
+ }, {}, {polling});
+ expect(timeDelta).not.toBeLessThan(polling);
});
-
- describe('Frame.waitForSelector', function() {
- const addElement = tag => document.body.appendChild(document.createElement(tag));
- 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');
- });
- 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(`
anything
`),
- ]);
- 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');
- 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 work when node is added through innerHTML', async({page, server}) => {
- await page.goto(server.EMPTY_PAGE);
- const watchdog = page.waitForSelector('h3 div');
- await page.evaluate(addElement, 'span');
- await page.evaluate(() => document.querySelector('span').innerHTML = '
');
- 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');
- 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');
- 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', { waitFor: 'visible' }).then(() => divFound = true);
- await page.setContent(`
1
`);
- 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 wait for visible recursively', async({page, server}) => {
- let divVisible = false;
- const waitForSelector = page.waitForSelector('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
- await page.setContent(`
`);
- 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(`
`);
- const waitForSelector = page.waitForSelector('div', { waitFor: '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(`
`);
- const waitForSelector = page.waitForSelector('div', { waitFor: '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(`
`);
- let divRemoved = false;
- const waitForSelector = page.waitForSelector('div', { waitFor: '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', { waitFor: 'hidden' });
- expect(handle).toBe(null);
- });
- it('should respect timeout', async({page, server}) => {
- let error = null;
- await page.waitForSelector('div', { timeout: 10 }).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting for selector "div" failed: timeout');
- 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(`
`);
- let error = null;
- await page.waitForSelector('div', { waitFor: 'hidden', timeout: 10 }).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
- });
- it('should respond to node attribute mutation', async({page, server}) => {
- let divFound = false;
- const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
- await page.setContent(`
`);
- 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(`
anything
`);
- 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.spec.js');
- });
- it('should throw for unknown waitFor option', async({page, server}) => {
- await page.setContent('
');
- const error = await page.waitForSelector('section', { waitFor: 'foo' }).catch(e => e);
- expect(error.message).toContain('Unsupported waitFor option');
- });
- it('should throw for visibility option', async({page, server}) => {
- await page.setContent('
');
- const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
- expect(error.message).toBe('options.visibility is not supported, did you mean options.waitFor?');
- });
- it('should throw for true waitFor option', async({page, server}) => {
- await page.setContent('
');
- const error = await page.waitForSelector('section', { waitFor: true }).catch(e => e);
- expect(error.message).toContain('Unsupported waitFor option');
- });
- it('should throw for false waitFor option', async({page, server}) => {
- await page.setContent('
');
- const error = await page.waitForSelector('section', { waitFor: false }).catch(e => e);
- expect(error.message).toContain('Unsupported waitFor option');
- });
- 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');
- 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('
');
- expect(await page.waitForSelector('css=div', { waitFor: 'detached'})).toBe(null);
- });
- it('should wait for detached', async({page, server}) => {
- await page.setContent('
');
- let done = false;
- const waitFor = page.waitForSelector('css=div', { waitFor: '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 poll on mutation', async({page, server}) => {
+ let success = false;
+ const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {}, {polling: 'mutation'})
+ .then(() => success = true);
+ await page.evaluate(() => window.__FOO = 'hit');
+ expect(success).toBe(false);
+ await page.evaluate(() => document.body.appendChild(document.createElement('div')));
+ await watchdog;
});
-
- 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(`
red herring
hello world
`);
- 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}) => {
- let error = null;
- await page.waitForSelector('//div', { timeout: 10 }).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting for selector "//div" failed: timeout');
- 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');
- 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(`
anything
`);
- 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(`
some text
`);
- const waitForXPath = page.waitForSelector('//html/body/div');
- expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
- });
+ 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 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('
');
+ 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}) => {
+ let error = null;
+ await page.waitForFunction('false', {}, {timeout: 10}).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting for function failed: timeout');
+ expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
+ });
+ it('should respect default timeout', async({page}) => {
+ page.setDefaultTimeout(1);
+ let error = null;
+ await page.waitForFunction('false').catch(e => error = e);
+ expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
+ expect(error.message).toContain('waiting for function failed: timeout');
+ });
+ 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;
+ });
+});
-};
+describe('Frame.waitForSelector', function() {
+ const addElement = tag => document.body.appendChild(document.createElement(tag));
+ 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');
+ });
+ 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(`
anything
`),
+ ]);
+ 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');
+ 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 work when node is added through innerHTML', async({page, server}) => {
+ await page.goto(server.EMPTY_PAGE);
+ const watchdog = page.waitForSelector('h3 div');
+ await page.evaluate(addElement, 'span');
+ await page.evaluate(() => document.querySelector('span').innerHTML = '
');
+ 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');
+ 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');
+ 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', { waitFor: 'visible' }).then(() => divFound = true);
+ await page.setContent(`
1
`);
+ 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 wait for visible recursively', async({page, server}) => {
+ let divVisible = false;
+ const waitForSelector = page.waitForSelector('div#inner', { waitFor: 'visible' }).then(() => divVisible = true);
+ await page.setContent(`
`);
+ 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(`
`);
+ const waitForSelector = page.waitForSelector('div', { waitFor: '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(`
`);
+ const waitForSelector = page.waitForSelector('div', { waitFor: '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(`
`);
+ let divRemoved = false;
+ const waitForSelector = page.waitForSelector('div', { waitFor: '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', { waitFor: 'hidden' });
+ expect(handle).toBe(null);
+ });
+ it('should respect timeout', async({page, server}) => {
+ let error = null;
+ await page.waitForSelector('div', { timeout: 10 }).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting for selector "div" failed: timeout');
+ 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(`
`);
+ let error = null;
+ await page.waitForSelector('div', { waitFor: 'hidden', timeout: 10 }).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting for selector "[hidden] div" failed: timeout');
+ });
+ it('should respond to node attribute mutation', async({page, server}) => {
+ let divFound = false;
+ const waitForSelector = page.waitForSelector('.zombo').then(() => divFound = true);
+ await page.setContent(`
`);
+ 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(`
anything
`);
+ 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.spec.js');
+ });
+ it('should throw for unknown waitFor option', async({page, server}) => {
+ await page.setContent('
');
+ const error = await page.waitForSelector('section', { waitFor: 'foo' }).catch(e => e);
+ expect(error.message).toContain('Unsupported waitFor option');
+ });
+ it('should throw for visibility option', async({page, server}) => {
+ await page.setContent('
');
+ const error = await page.waitForSelector('section', { visibility: 'hidden' }).catch(e => e);
+ expect(error.message).toBe('options.visibility is not supported, did you mean options.waitFor?');
+ });
+ it('should throw for true waitFor option', async({page, server}) => {
+ await page.setContent('
');
+ const error = await page.waitForSelector('section', { waitFor: true }).catch(e => e);
+ expect(error.message).toContain('Unsupported waitFor option');
+ });
+ it('should throw for false waitFor option', async({page, server}) => {
+ await page.setContent('
');
+ const error = await page.waitForSelector('section', { waitFor: false }).catch(e => e);
+ expect(error.message).toContain('Unsupported waitFor option');
+ });
+ 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');
+ 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('
');
+ expect(await page.waitForSelector('css=div', { waitFor: 'detached'})).toBe(null);
+ });
+ it('should wait for detached', async({page, server}) => {
+ await page.setContent('
');
+ let done = false;
+ const waitFor = page.waitForSelector('css=div', { waitFor: '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(`
red herring
hello world
`);
+ 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}) => {
+ let error = null;
+ await page.waitForSelector('//div', { timeout: 10 }).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting for selector "//div" failed: timeout');
+ 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');
+ 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(`
anything
`);
+ 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(`
some text
`);
+ const waitForXPath = page.waitForSelector('//html/body/div');
+ expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
+ });
+});
diff --git a/test/workers.spec.js b/test/workers.spec.js
index f3cba12171..eee6f8d81d 100644
--- a/test/workers.spec.js
+++ b/test/workers.spec.js
@@ -15,126 +15,122 @@
* limitations under the License.
*/
-/**
- * @type {PageTestSuite}
- */
-module.exports.describe = function({FFOX, CHROMIUM, WEBKIT, LINUX}) {
+const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType);
- 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');
+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');
+ 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');
- 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));
- page.evaluate(() => new Worker(URL.createObjectURL(new Blob([`setTimeout(() => { 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');
- 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');
- 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();
- });
+ 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');
+ 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));
+ page.evaluate(() => new Worker(URL.createObjectURL(new Blob([`setTimeout(() => { 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');
+ 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');
+ 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();
+ });
+});