feat(Page): introduce Page.waitForXPath (#1767)
This patch: - introduces `page.waitForXPath` method - introduces `frame.waitForXPath` method - amends `page.waitFor` to treat strings that start with `//` as xpath queries. Fixes #1757.
This commit is contained in:
parent
62597bf897
commit
cb684ebbc4
66
docs/api.md
66
docs/api.md
@ -99,6 +99,7 @@
|
|||||||
* [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
|
* [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
|
||||||
* [page.waitForNavigation(options)](#pagewaitfornavigationoptions)
|
* [page.waitForNavigation(options)](#pagewaitfornavigationoptions)
|
||||||
* [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
|
* [page.waitForSelector(selector[, options])](#pagewaitforselectorselector-options)
|
||||||
|
* [page.waitForXPath(xpath[, options])](#pagewaitforxpathxpath-options)
|
||||||
- [class: Keyboard](#class-keyboard)
|
- [class: Keyboard](#class-keyboard)
|
||||||
* [keyboard.down(key[, options])](#keyboarddownkey-options)
|
* [keyboard.down(key[, options])](#keyboarddownkey-options)
|
||||||
* [keyboard.press(key[, options])](#keyboardpresskey-options)
|
* [keyboard.press(key[, options])](#keyboardpresskey-options)
|
||||||
@ -147,6 +148,7 @@
|
|||||||
* [frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args)
|
* [frame.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#framewaitforselectororfunctionortimeout-options-args)
|
||||||
* [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args)
|
* [frame.waitForFunction(pageFunction[, options[, ...args]])](#framewaitforfunctionpagefunction-options-args)
|
||||||
* [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
|
* [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
|
||||||
|
* [frame.waitForXPath(xpath[, options])](#framewaitforxpathxpath-options)
|
||||||
- [class: ExecutionContext](#class-executioncontext)
|
- [class: ExecutionContext](#class-executioncontext)
|
||||||
* [executionContext.evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args)
|
* [executionContext.evaluate(pageFunction, ...args)](#executioncontextevaluatepagefunction-args)
|
||||||
* [executionContext.evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args)
|
* [executionContext.evaluateHandle(pageFunction, ...args)](#executioncontextevaluatehandlepagefunction-args)
|
||||||
@ -1209,7 +1211,8 @@ This is a shortcut for [page.mainFrame().url()](#frameurl)
|
|||||||
- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
|
- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
|
||||||
|
|
||||||
This method behaves differently with respect to the type of the first parameter:
|
This method behaves differently with respect to the type of the first parameter:
|
||||||
- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] to wait for and the method is a shortcut for [page.waitForSelector](#pagewaitforselectorselector-options)
|
- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] or [xpath], depending on whether or not it starts with '//', and the method is a shortcut for
|
||||||
|
[page.waitForSelector](#pagewaitforselectorselector-options) or [page.waitForXPath](#pagewaitforxpathxpath-options)
|
||||||
- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [page.waitForFunction()](#pagewaitforfunctionpagefunction-options-args).
|
- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [page.waitForFunction()](#pagewaitforfunctionpagefunction-options-args).
|
||||||
- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
|
- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
|
||||||
- otherwise, an exception is thrown
|
- otherwise, an exception is thrown
|
||||||
@ -1279,6 +1282,35 @@ puppeteer.launch().then(async browser => {
|
|||||||
```
|
```
|
||||||
Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectorselector-options).
|
Shortcut for [page.mainFrame().waitForSelector(selector[, options])](#framewaitforselectorselector-options).
|
||||||
|
|
||||||
|
#### page.waitForXPath(xpath[, options])
|
||||||
|
- `xpath` <[string]> A [xpath] of an element to wait for,
|
||||||
|
- `options` <[Object]> Optional waiting parameters
|
||||||
|
- `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
|
||||||
|
- `hidden` <[boolean]> wait for element to not be found in the DOM or to be hidden, i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
|
||||||
|
- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds).
|
||||||
|
- returns: <[Promise]<[ElementHandle]>> Promise which resolves when element specified by xpath string is added to DOM.
|
||||||
|
|
||||||
|
Wait for the `xpath` to appear in page. If at the moment of calling
|
||||||
|
the method the `xpath` already exists, the method will return
|
||||||
|
immediately. If the xpath doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
|
||||||
|
|
||||||
|
This method works across navigations:
|
||||||
|
```js
|
||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
|
||||||
|
puppeteer.launch().then(async browser => {
|
||||||
|
const page = await browser.newPage();
|
||||||
|
let currentURL;
|
||||||
|
page
|
||||||
|
.waitForXPath('//img')
|
||||||
|
.then(() => console.log('First URL with image: ' + currentURL));
|
||||||
|
for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])
|
||||||
|
await page.goto(currentURL);
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
Shortcut for [page.mainFrame().waitForXPath(xpath[, options])](#framewaitforxpathxpath-options).
|
||||||
|
|
||||||
|
|
||||||
### class: Keyboard
|
### class: Keyboard
|
||||||
|
|
||||||
@ -1677,7 +1709,8 @@ Returns frame's url.
|
|||||||
- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
|
- returns: <[Promise]<[JSHandle]>> Promise which resolves to a JSHandle of the success value
|
||||||
|
|
||||||
This method behaves differently with respect to the type of the first parameter:
|
This method behaves differently with respect to the type of the first parameter:
|
||||||
- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] to wait for and the method is a shortcut for [frame.waitForSelector](#framewaitforselectorselector-options)
|
- if `selectorOrFunctionOrTimeout` is a `string`, then the first argument is treated as a [selector] or [xpath], depending on whether or not it starts with '//', and the method is a shortcut for
|
||||||
|
[frame.waitForSelector](#framewaitforselectorselector-options) or [frame.waitForXPath](#framewaitforsxpathxpath-options)
|
||||||
- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [frame.waitForFunction()](#framewaitforfunctionpagefunction-options-args).
|
- if `selectorOrFunctionOrTimeout` is a `function`, then the first argument is treated as a predicate to wait for and the method is a shortcut for [frame.waitForFunction()](#framewaitforfunctionpagefunction-options-args).
|
||||||
- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
|
- if `selectorOrFunctionOrTimeout` is a `number`, then the first argument is treated as a timeout in milliseconds and the method returns a promise which resolves after the timeout
|
||||||
- otherwise, an exception is thrown
|
- otherwise, an exception is thrown
|
||||||
@ -1734,6 +1767,34 @@ puppeteer.launch().then(async browser => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### frame.waitForXPath(xpath[, options])
|
||||||
|
- `xpath` <[string]> A [xpath] of an element to wait for
|
||||||
|
- `options` <[Object]> Optional waiting parameters
|
||||||
|
- `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
|
||||||
|
- `hidden` <[boolean]> wait for element to not be found in the DOM or to be hidden, i.e. have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
|
||||||
|
- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds).
|
||||||
|
- returns: <[Promise]<[ElementHandle]>> Promise which resolves when element specified by xpath string is added to DOM.
|
||||||
|
|
||||||
|
Wait for the `xpath` to appear in page. If at the moment of calling
|
||||||
|
the method the `xpath` already exists, the method will return
|
||||||
|
immediately. If the xpath doesn't appear after the `timeout` milliseconds of waiting, the function will throw.
|
||||||
|
|
||||||
|
This method works across navigations:
|
||||||
|
```js
|
||||||
|
const puppeteer = require('puppeteer');
|
||||||
|
|
||||||
|
puppeteer.launch().then(async browser => {
|
||||||
|
const page = await browser.newPage();
|
||||||
|
let currentURL;
|
||||||
|
page.mainFrame()
|
||||||
|
.waitForXPath('//img')
|
||||||
|
.then(() => console.log('First URL with image: ' + currentURL));
|
||||||
|
for (currentURL of ['https://example.com', 'https://google.com', 'https://bbc.com'])
|
||||||
|
await page.goto(currentURL);
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### class: ExecutionContext
|
### class: ExecutionContext
|
||||||
|
|
||||||
The class represents a context for JavaScript execution. Examples of JavaScript contexts are:
|
The class represents a context for JavaScript execution. Examples of JavaScript contexts are:
|
||||||
@ -2340,3 +2401,4 @@ reported.
|
|||||||
[Touchscreen]: #class-touchscreen "Touchscreen"
|
[Touchscreen]: #class-touchscreen "Touchscreen"
|
||||||
[Target]: #class-target "Target"
|
[Target]: #class-target "Target"
|
||||||
[USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout"
|
[USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout"
|
||||||
|
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"
|
||||||
|
@ -566,8 +566,14 @@ class Frame {
|
|||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
|
waitFor(selectorOrFunctionOrTimeout, options = {}, ...args) {
|
||||||
if (helper.isString(selectorOrFunctionOrTimeout))
|
const xPathPattern = '//';
|
||||||
return this.waitForSelector(/** @type {string} */(selectorOrFunctionOrTimeout), options);
|
|
||||||
|
if (helper.isString(selectorOrFunctionOrTimeout)) {
|
||||||
|
const string = /** @type {string} */ (selectorOrFunctionOrTimeout);
|
||||||
|
if (string.startsWith(xPathPattern))
|
||||||
|
return this.waitForXPath(string, options);
|
||||||
|
return this.waitForSelector(string, options);
|
||||||
|
}
|
||||||
if (helper.isNumber(selectorOrFunctionOrTimeout))
|
if (helper.isNumber(selectorOrFunctionOrTimeout))
|
||||||
return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout));
|
return new Promise(fulfill => setTimeout(fulfill, selectorOrFunctionOrTimeout));
|
||||||
if (typeof selectorOrFunctionOrTimeout === 'function')
|
if (typeof selectorOrFunctionOrTimeout === 'function')
|
||||||
@ -581,37 +587,16 @@ class Frame {
|
|||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
*/
|
*/
|
||||||
waitForSelector(selector, options = {}) {
|
waitForSelector(selector, options = {}) {
|
||||||
const timeout = options.timeout || 30000;
|
return this._waitForSelectorOrXPath(selector, false, options);
|
||||||
const waitForVisible = !!options.visible;
|
}
|
||||||
const waitForHidden = !!options.hidden;
|
|
||||||
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
|
|
||||||
return this.waitForFunction(predicate, {timeout, polling}, selector, waitForVisible, waitForHidden);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} selector
|
* @param {string} xpath
|
||||||
* @param {boolean} waitForVisible
|
* @param {!Object=} options
|
||||||
* @param {boolean} waitForHidden
|
* @return {!Promise}
|
||||||
* @return {?Node|boolean}
|
*/
|
||||||
*/
|
waitForXPath(xpath, options = {}) {
|
||||||
function predicate(selector, waitForVisible, waitForHidden) {
|
return this._waitForSelectorOrXPath(xpath, true, options);
|
||||||
const node = document.querySelector(selector);
|
|
||||||
if (!node)
|
|
||||||
return waitForHidden;
|
|
||||||
if (!waitForVisible && !waitForHidden)
|
|
||||||
return node;
|
|
||||||
const style = window.getComputedStyle(node);
|
|
||||||
const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
|
|
||||||
const success = (waitForVisible === isVisible || waitForHidden === !isVisible);
|
|
||||||
return success ? node : null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
function hasVisibleBoundingBox() {
|
|
||||||
const rect = node.getBoundingClientRect();
|
|
||||||
return !!(rect.top || rect.bottom || rect.width || rect.height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -632,6 +617,51 @@ class Frame {
|
|||||||
return this.evaluate(() => document.title);
|
return this.evaluate(() => document.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} selectorOrXPath
|
||||||
|
* @param {boolean} isXPath
|
||||||
|
* @param {!Object=} options
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
_waitForSelectorOrXPath(selectorOrXPath, isXPath, options = {}) {
|
||||||
|
const timeout = options.timeout || 30000;
|
||||||
|
const waitForVisible = !!options.visible;
|
||||||
|
const waitForHidden = !!options.hidden;
|
||||||
|
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
|
||||||
|
return this.waitForFunction(predicate, {timeout, polling}, selectorOrXPath, isXPath, waitForVisible, waitForHidden);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} selectorOrXPath
|
||||||
|
* @param {boolean} isXPath
|
||||||
|
* @param {boolean} waitForVisible
|
||||||
|
* @param {boolean} waitForHidden
|
||||||
|
* @return {?Node|boolean}
|
||||||
|
*/
|
||||||
|
function predicate(selectorOrXPath, isXPath, waitForVisible, waitForHidden) {
|
||||||
|
const node = isXPath
|
||||||
|
? document.evaluate(selectorOrXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
|
||||||
|
: document.querySelector(selectorOrXPath);
|
||||||
|
if (!node)
|
||||||
|
return waitForHidden;
|
||||||
|
if (!waitForVisible && !waitForHidden)
|
||||||
|
return node;
|
||||||
|
const element = /** @type {Element} */ (node.nodeType === Node.TEXT_NODE ? node.parentElement : node);
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(element);
|
||||||
|
const isVisible = style && style.visibility !== 'hidden' && hasVisibleBoundingBox();
|
||||||
|
const success = (waitForVisible === isVisible || waitForHidden === !isVisible);
|
||||||
|
return success ? node : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
function hasVisibleBoundingBox() {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
return !!(rect.top || rect.bottom || rect.width || rect.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} framePayload
|
* @param {!Object} framePayload
|
||||||
*/
|
*/
|
||||||
@ -654,7 +684,7 @@ class Frame {
|
|||||||
|
|
||||||
_detach() {
|
_detach() {
|
||||||
for (const waitTask of this._waitTasks)
|
for (const waitTask of this._waitTasks)
|
||||||
waitTask.terminate(new Error('waitForSelector failed: frame got detached.'));
|
waitTask.terminate(new Error('waitForFunction failed: frame got detached.'));
|
||||||
this._detached = true;
|
this._detached = true;
|
||||||
if (this._parentFrame)
|
if (this._parentFrame)
|
||||||
this._parentFrame._childFrames.delete(this);
|
this._parentFrame._childFrames.delete(this);
|
||||||
|
@ -881,6 +881,15 @@ class Page extends EventEmitter {
|
|||||||
return this.mainFrame().waitForSelector(selector, options);
|
return this.mainFrame().waitForSelector(selector, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} xpath
|
||||||
|
* @param {!Object=} options
|
||||||
|
* @return {!Promise}
|
||||||
|
*/
|
||||||
|
waitForXPath(xpath, options = {}) {
|
||||||
|
return this.mainFrame().waitForXPath(xpath, options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function()} pageFunction
|
* @param {function()} pageFunction
|
||||||
* @param {!Object=} options
|
* @param {!Object=} options
|
||||||
|
84
test/test.js
84
test/test.js
@ -797,7 +797,7 @@ describe('Page', function() {
|
|||||||
await FrameUtils.detachFrame(page, 'frame1');
|
await FrameUtils.detachFrame(page, 'frame1');
|
||||||
await waitPromise;
|
await waitPromise;
|
||||||
expect(waitError).toBeTruthy();
|
expect(waitError).toBeTruthy();
|
||||||
expect(waitError.message).toContain('waitForSelector failed: frame got detached.');
|
expect(waitError.message).toContain('waitForFunction failed: frame got detached.');
|
||||||
});
|
});
|
||||||
it('should survive cross-process navigation', async({page, server}) => {
|
it('should survive cross-process navigation', async({page, server}) => {
|
||||||
let boxFound = false;
|
let boxFound = false;
|
||||||
@ -884,6 +884,73 @@ describe('Page', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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(`<p>red herring</p><p>hello world </p>`);
|
||||||
|
const waitForXPath = page.waitForXPath('//p[normalize-space(.)="hello world"]');
|
||||||
|
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('hello world ');
|
||||||
|
});
|
||||||
|
it('should run in specified frame', async({page, server}) => {
|
||||||
|
await FrameUtils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||||
|
await FrameUtils.attachFrame(page, 'frame2', server.EMPTY_PAGE);
|
||||||
|
const frame1 = page.frames()[1];
|
||||||
|
const frame2 = page.frames()[2];
|
||||||
|
let added = false;
|
||||||
|
frame2.waitForXPath('//div').then(() => added = true);
|
||||||
|
expect(added).toBe(false);
|
||||||
|
await frame1.evaluate(addElement, 'div');
|
||||||
|
expect(added).toBe(false);
|
||||||
|
await frame2.evaluate(addElement, 'div');
|
||||||
|
expect(added).toBe(true);
|
||||||
|
});
|
||||||
|
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 FrameUtils.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 FrameUtils.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(`<div style='display: block;'></div>`);
|
||||||
|
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(`<div class='zombo'>anything</div>`);
|
||||||
|
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(`<div>some text</div>`);
|
||||||
|
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(`<div>some text</div>`);
|
||||||
|
const waitForXPath = page.waitForXPath('/html/body/div');
|
||||||
|
expect(await page.evaluate(x => x.textContent, await waitForXPath)).toBe('some text');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Page.waitFor', function() {
|
describe('Page.waitFor', function() {
|
||||||
it('should wait for selector', async({page, server}) => {
|
it('should wait for selector', async({page, server}) => {
|
||||||
let found = false;
|
let found = false;
|
||||||
@ -894,6 +961,21 @@ describe('Page', function() {
|
|||||||
await waitFor;
|
await waitFor;
|
||||||
expect(found).toBe(true);
|
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(`<div>some text</div>`);
|
||||||
|
let error = null;
|
||||||
|
await page.waitFor('/html/body/div').catch(e => error = e);
|
||||||
|
expect(error).toBeTruthy();
|
||||||
|
});
|
||||||
it('should timeout', async({page, server}) => {
|
it('should timeout', async({page, server}) => {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const timeout = 42;
|
const timeout = 42;
|
||||||
|
Loading…
Reference in New Issue
Block a user