diff --git a/test/frame.spec.js b/test/frame.spec.js
index 8d881fd3..e4b9ee9d 100644
--- a/test/frame.spec.js
+++ b/test/frame.spec.js
@@ -15,7 +15,6 @@
*/
const utils = require('./utils');
-const {TimeoutError} = utils.requireRoot('Errors');
module.exports.addTests = function({testRunner, expect}) {
const {describe, xdescribe, fdescribe} = testRunner;
@@ -157,348 +156,6 @@ module.exports.addTests = function({testRunner, expect}) {
});
});
- 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.evaluateOnNewDocument(() => window.__RELOADED = true);
- await page.waitForFunction(() => {
- if (!window.__RELOADED)
- window.location.reload();
- return true;
- });
- });
- it('should poll on interval', async({page, server}) => {
- let success = false;
- const startTime = Date.now();
- const polling = 100;
- const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling})
- .then(() => success = true);
- await page.evaluate(() => window.__FOO = 'hit');
- expect(success).toBe(false);
- await page.evaluate(() => document.body.appendChild(document.createElement('div')));
- await watchdog;
- expect(Date.now() - startTime).not.toBeLessThan(polling / 2);
- });
- 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);
- const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'});
- await page.evaluate(() => window.__FOO = 'hit');
- await watchdog;
- });
- 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(TimeoutError);
- });
- 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;
- });
- });
-
- 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 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.waitForSelector 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(eHandle.executionContext().frame()).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(eHandle.executionContext().frame()).toBe(frame2);
- });
-
- it('should throw if evaluation failed', async({page, server}) => {
- await page.evaluateOnNewDocument(function() {
- document.querySelector = null;
- });
- await page.goto(server.EMPTY_PAGE);
- let error = null;
- await page.waitForSelector('*').catch(e => error = e);
- expect(error.message).toContain('document.querySelector is not a function');
- });
- 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', {visible: true}).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', {visible: true}).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 visibility: hidden', async({page, server}) => {
- let divHidden = false;
- await page.setContent(``);
- const waitForSelector = page.waitForSelector('div', {hidden: true}).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', {hidden: true}).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', {hidden: true}).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 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(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', {hidden: true, timeout: 10}).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting for selector "div" to be hidden 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('frame.spec.js');
- });
- });
-
- describe('Frame.waitForXPath', 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.waitForXPath('//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.waitForXPath('//div', {timeout: 10}).catch(e => error = e);
- expect(error).toBeTruthy();
- expect(error.message).toContain('waiting for XPath "//div" failed: timeout');
- expect(error).toBeInstanceOf(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.waitForXPath('//div');
- await frame1.evaluate(addElement, 'div');
- await frame2.evaluate(addElement, 'div');
- const eHandle = await waitForXPathPromise;
- expect(eHandle.executionContext().frame()).toBe(frame2);
- });
- it('should throw if evaluation failed', async({page, server}) => {
- await page.evaluateOnNewDocument(function() {
- document.evaluate = null;
- });
- await page.goto(server.EMPTY_PAGE);
- let error = null;
- await page.waitForXPath('*').catch(e => error = e);
- expect(error.message).toContain('document.evaluate is not a function');
- });
- 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.waitForXPath('//*[@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('hidden should wait for display: none', async({page, server}) => {
- let divHidden = false;
- await page.setContent(``);
- const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true);
- await page.waitForXPath('//div'); // do a round trip
- expect(divHidden).toBe(false);
- await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
- expect(await waitForXPath).toBe(true);
- expect(divHidden).toBe(true);
- });
- it('should return the element handle', async({page, server}) => {
- const waitForXPath = page.waitForXPath('//*[@class="zombo"]');
- await page.setContent(`anything
`);
- expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
- });
- it('should allow you to select a text node', async({page, server}) => {
- await page.setContent(`some text
`);
- const text = await page.waitForXPath('//div/text()');
- expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */);
- });
- it('should allow you to select an element with single slash', async({page, server}) => {
- await page.setContent(`some text
`);
- const waitForXPath = page.waitForXPath('/html/body/div');
- expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
- });
- });
-
describe('Frame Management', function() {
it('should handle nested frames', async({page, server}) => {
await page.goto(server.PREFIX + '/frames/nested-frames.html');
diff --git a/test/page.spec.js b/test/page.spec.js
index 584af611..a9a78f2d 100644
--- a/test/page.spec.js
+++ b/test/page.spec.js
@@ -401,52 +401,6 @@ module.exports.addTests = function({testRunner, expect, headless}) {
});
});
- 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 wait for predicate', async({page, server}) => {
- const watchdog = page.waitFor(() => window.innerWidth < 100);
- page.setViewport({width: 10, height: 10});
- await watchdog;
- });
- 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, {}, 1, 2);
- });
- });
-
describe('Page.Events.Console', function() {
it('should work', async({page, server}) => {
let message = null;
diff --git a/test/test.js b/test/test.js
index b4ecbcfe..2e0d3128 100644
--- a/test/test.js
+++ b/test/test.js
@@ -151,6 +151,7 @@ describe('Browser', function() {
require('./coverage.spec.js').addTests({testRunner, expect});
require('./elementhandle.spec.js').addTests({testRunner, expect});
require('./queryselector.spec.js').addTests({testRunner, expect});
+ require('./waittask.spec.js').addTests({testRunner, expect});
require('./frame.spec.js').addTests({testRunner, expect});
require('./input.spec.js').addTests({testRunner, expect});
require('./jshandle.spec.js').addTests({testRunner, expect});
diff --git a/test/waittask.spec.js b/test/waittask.spec.js
new file mode 100644
index 00000000..a8dcf91f
--- /dev/null
+++ b/test/waittask.spec.js
@@ -0,0 +1,413 @@
+/**
+ * Copyright 2018 Google Inc. All rights reserved.
+ *
+ * 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 {TimeoutError} = utils.requireRoot('Errors');
+
+module.exports.addTests = function({testRunner, expect, product}) {
+ const {describe, xdescribe, fdescribe} = testRunner;
+ const {it, fit, xit} = testRunner;
+ const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
+
+ 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 wait for predicate', async({page, server}) => {
+ const watchdog = page.waitFor(() => window.innerWidth < 100);
+ page.setViewport({width: 10, height: 10});
+ await watchdog;
+ });
+ 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, {}, 1, 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.evaluateOnNewDocument(() => window.__RELOADED = true);
+ await page.waitForFunction(() => {
+ if (!window.__RELOADED)
+ window.location.reload();
+ return true;
+ });
+ });
+ it('should poll on interval', async({page, server}) => {
+ let success = false;
+ const startTime = Date.now();
+ const polling = 100;
+ const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling})
+ .then(() => success = true);
+ await page.evaluate(() => window.__FOO = 'hit');
+ expect(success).toBe(false);
+ await page.evaluate(() => document.body.appendChild(document.createElement('div')));
+ await watchdog;
+ expect(Date.now() - startTime).not.toBeLessThan(polling / 2);
+ });
+ 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);
+ const watchdog = page.waitForFunction(() => window.__FOO === 'hit', {polling: 'raf'});
+ await page.evaluate(() => window.__FOO = 'hit');
+ await watchdog;
+ });
+ 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(TimeoutError);
+ });
+ 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;
+ });
+ });
+
+ 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 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.waitForSelector 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(eHandle.executionContext().frame()).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(eHandle.executionContext().frame()).toBe(frame2);
+ });
+
+ it('should throw if evaluation failed', async({page, server}) => {
+ await page.evaluateOnNewDocument(function() {
+ document.querySelector = null;
+ });
+ await page.goto(server.EMPTY_PAGE);
+ let error = null;
+ await page.waitForSelector('*').catch(e => error = e);
+ expect(error.message).toContain('document.querySelector is not a function');
+ });
+ 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', {visible: true}).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', {visible: true}).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 visibility: hidden', async({page, server}) => {
+ let divHidden = false;
+ await page.setContent(``);
+ const waitForSelector = page.waitForSelector('div', {hidden: true}).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', {hidden: true}).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', {hidden: true}).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 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(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', {hidden: true, timeout: 10}).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting for selector "div" to be hidden 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');
+ });
+ });
+
+ describe('Frame.waitForXPath', 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.waitForXPath('//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.waitForXPath('//div', {timeout: 10}).catch(e => error = e);
+ expect(error).toBeTruthy();
+ expect(error.message).toContain('waiting for XPath "//div" failed: timeout');
+ expect(error).toBeInstanceOf(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.waitForXPath('//div');
+ await frame1.evaluate(addElement, 'div');
+ await frame2.evaluate(addElement, 'div');
+ const eHandle = await waitForXPathPromise;
+ expect(eHandle.executionContext().frame()).toBe(frame2);
+ });
+ it('should throw if evaluation failed', async({page, server}) => {
+ await page.evaluateOnNewDocument(function() {
+ document.evaluate = null;
+ });
+ await page.goto(server.EMPTY_PAGE);
+ let error = null;
+ await page.waitForXPath('*').catch(e => error = e);
+ expect(error.message).toContain('document.evaluate is not a function');
+ });
+ 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.waitForXPath('//*[@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('hidden should wait for display: none', async({page, server}) => {
+ let divHidden = false;
+ await page.setContent(``);
+ const waitForXPath = page.waitForXPath('//div', {hidden: true}).then(() => divHidden = true);
+ await page.waitForXPath('//div'); // do a round trip
+ expect(divHidden).toBe(false);
+ await page.evaluate(() => document.querySelector('div').style.setProperty('display', 'none'));
+ expect(await waitForXPath).toBe(true);
+ expect(divHidden).toBe(true);
+ });
+ it('should return the element handle', async({page, server}) => {
+ const waitForXPath = page.waitForXPath('//*[@class="zombo"]');
+ await page.setContent(`anything
`);
+ expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('anything');
+ });
+ it('should allow you to select a text node', async({page, server}) => {
+ await page.setContent(`some text
`);
+ const text = await page.waitForXPath('//div/text()');
+ expect(await (await text.getProperty('nodeType')).jsonValue()).toBe(3 /* Node.TEXT_NODE */);
+ });
+ it('should allow you to select an element with single slash', async({page, server}) => {
+ await page.setContent(`some text
`);
+ const waitForXPath = page.waitForXPath('/html/body/div');
+ expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
+ });
+ });
+
+};