From 53c91348094dc0bce59086c98986c5d06a949d08 Mon Sep 17 00:00:00 2001 From: Nikolay Vitkov <34244704+Lightning00Blade@users.noreply.github.com> Date: Fri, 2 Feb 2024 13:18:43 +0100 Subject: [PATCH] refactor!: remove `$x` and `waitForXpath` (#11782) --- docs/api/puppeteer.elementhandle._x.md | 33 --- docs/api/puppeteer.elementhandle.md | 2 - .../puppeteer.elementhandle.waitforxpath.md | 79 ------ docs/api/puppeteer.frame._x.md | 31 --- docs/api/puppeteer.frame.md | 2 - docs/api/puppeteer.frame.waitforxpath.md | 39 --- docs/api/puppeteer.page._x.md | 29 -- docs/api/puppeteer.page.md | 2 - docs/api/puppeteer.page.waitforxpath.md | 65 ----- .../puppeteer-core/src/api/ElementHandle.ts | 99 ------- packages/puppeteer-core/src/api/Frame.ts | 50 ---- packages/puppeteer-core/src/api/Page.ts | 72 ----- test/TestExpectations.json | 8 +- test/src/elementhandle.spec.ts | 24 +- test/src/queryselector.spec.ts | 91 ++++--- test/src/waittask.spec.ts | 252 +++++++++--------- .../api/puppeteer.frame.waitfortimeout.md | 2 +- .../api/puppeteer.page.waitfortimeout.md | 2 +- 18 files changed, 193 insertions(+), 689 deletions(-) delete mode 100644 docs/api/puppeteer.elementhandle._x.md delete mode 100644 docs/api/puppeteer.elementhandle.waitforxpath.md delete mode 100644 docs/api/puppeteer.frame._x.md delete mode 100644 docs/api/puppeteer.frame.waitforxpath.md delete mode 100644 docs/api/puppeteer.page._x.md delete mode 100644 docs/api/puppeteer.page.waitforxpath.md diff --git a/docs/api/puppeteer.elementhandle._x.md b/docs/api/puppeteer.elementhandle._x.md deleted file mode 100644 index 1aa5a7f01f1..00000000000 --- a/docs/api/puppeteer.elementhandle._x.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -sidebar_label: ElementHandle.$x ---- - -# ElementHandle.$x() method - -> Warning: This API is now obsolete. -> -> Use [ElementHandle.$$()](./puppeteer.elementhandle.__.md) with the `xpath` prefix. -> -> Example: `await elementHandle.$$('xpath/' + xpathExpression)` -> -> The method evaluates the XPath expression relative to the elementHandle. If `xpath` starts with `//` instead of `.//`, the dot will be appended automatically. -> -> If there are no such elements, the method will resolve to an empty array. - -#### Signature: - -```typescript -class ElementHandle { - $x(expression: string): Promise>>; -} -``` - -## Parameters - -| Parameter | Type | Description | -| ---------- | ------ | -------------------------------------------------------------------------------------------- | -| expression | string | Expression to [evaluate](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate) | - -**Returns:** - -Promise<Array<[ElementHandle](./puppeteer.elementhandle.md)<Node>>> diff --git a/docs/api/puppeteer.elementhandle.md b/docs/api/puppeteer.elementhandle.md index b47b5abf450..6691d089899 100644 --- a/docs/api/puppeteer.elementhandle.md +++ b/docs/api/puppeteer.elementhandle.md @@ -53,7 +53,6 @@ The constructor for this class is marked as internal. Third-party code should no | [$$(selector)](./puppeteer.elementhandle.__.md) | | Queries the current element for all elements matching the given selector. | | [$$eval(selector, pageFunction, args)](./puppeteer.elementhandle.__eval.md) | |

Runs the given function on an array of elements matching the given selector in the current element.

If the given function returns a promise, then this method will wait till the promise resolves.

| | [$eval(selector, pageFunction, args)](./puppeteer.elementhandle._eval.md) | |

Runs the given function on the first element matching the given selector in the current element.

If the given function returns a promise, then this method will wait till the promise resolves.

| -| [$x(expression)](./puppeteer.elementhandle._x.md) | | | | [autofill(data)](./puppeteer.elementhandle.autofill.md) | | If the element is a form input, you can use [ElementHandle.autofill()](./puppeteer.elementhandle.autofill.md) to test if the form is compatible with the browser's autofill implementation. Throws an error if the form cannot be autofilled. | | [boundingBox()](./puppeteer.elementhandle.boundingbox.md) | | This method returns the bounding box of the element (relative to the main frame), or null if the element is [not part of the layout](https://drafts.csswg.org/css-display-4/#box-generation) (example: display: none). | | [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or null if the element is [not part of the layout](https://drafts.csswg.org/css-display-4/#box-generation) (example: display: none). | @@ -85,4 +84,3 @@ The constructor for this class is marked as internal. Third-party code should no | [type(text, options)](./puppeteer.elementhandle.type.md) | |

Focuses the element, and then sends a keydown, keypress/input, and keyup event for each character in the text.

To press a special key, like Control or ArrowDown, use [ElementHandle.press()](./puppeteer.elementhandle.press.md).

| | [uploadFile(this, paths)](./puppeteer.elementhandle.uploadfile.md) | | Sets the value of an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) to the given file paths. | | [waitForSelector(selector, options)](./puppeteer.elementhandle.waitforselector.md) | |

Wait for an element matching the given selector to appear in the current element.

Unlike [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), this method does not work across navigations or if the element is detached from DOM.

| -| [waitForXPath(xpath, options)](./puppeteer.elementhandle.waitforxpath.md) | | | diff --git a/docs/api/puppeteer.elementhandle.waitforxpath.md b/docs/api/puppeteer.elementhandle.waitforxpath.md deleted file mode 100644 index 7e071581190..00000000000 --- a/docs/api/puppeteer.elementhandle.waitforxpath.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -sidebar_label: ElementHandle.waitForXPath ---- - -# ElementHandle.waitForXPath() method - -> Warning: This API is now obsolete. -> -> Use [ElementHandle.waitForSelector()](./puppeteer.elementhandle.waitforselector.md) with the `xpath` prefix. -> -> Example: `await elementHandle.waitForSelector('xpath/' + xpathExpression)` -> -> The method evaluates the XPath expression relative to the elementHandle. -> -> Wait for the `xpath` within the element. 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. -> -> If `xpath` starts with `//` instead of `.//`, the dot will be appended automatically. - -#### Signature: - -```typescript -class ElementHandle { - waitForXPath( - xpath: string, - options?: { - visible?: boolean; - hidden?: boolean; - timeout?: number; - } - ): Promise | null>; -} -``` - -## Parameters - -| Parameter | Type | Description | -| --------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| xpath | string | A [xpath](https://developer.mozilla.org/en-US/docs/Web/XPath) of an element to wait for | -| options | { visible?: boolean; hidden?: boolean; timeout?: number; } | _(Optional)_ Optional waiting parameters | - -**Returns:** - -Promise<[ElementHandle](./puppeteer.elementhandle.md)<Node> \| null> - -Promise which resolves when element specified by xpath string is added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is not found in DOM, otherwise resolves to `ElementHandle`. - -## Remarks - -The optional Argument `options` have properties: - -- `visible`: A boolean to 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`: A 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`: A number which is maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md) method. - -## Example - -This method works across navigation. - -```ts -import puppeteer from 'puppeteer'; -(async () => { - const browser = await puppeteer.launch(); - 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(); -})(); -``` diff --git a/docs/api/puppeteer.frame._x.md b/docs/api/puppeteer.frame._x.md deleted file mode 100644 index 380a488bbc0..00000000000 --- a/docs/api/puppeteer.frame._x.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_label: Frame.$x ---- - -# Frame.$x() method - -> Warning: This API is now obsolete. -> -> Use [Frame.$$()](./puppeteer.frame.__.md) with the `xpath` prefix. -> -> Example: `await frame.$$('xpath/' + xpathExpression)` -> -> This method evaluates the given XPath expression and returns the results. If `xpath` starts with `//` instead of `.//`, the dot will be appended automatically. - -#### Signature: - -```typescript -class Frame { - $x(expression: string): Promise>>; -} -``` - -## Parameters - -| Parameter | Type | Description | -| ---------- | ------ | --------------------------------- | -| expression | string | the XPath expression to evaluate. | - -**Returns:** - -Promise<Array<[ElementHandle](./puppeteer.elementhandle.md)<Node>>> diff --git a/docs/api/puppeteer.frame.md b/docs/api/puppeteer.frame.md index f2bed31d82c..b3b422be702 100644 --- a/docs/api/puppeteer.frame.md +++ b/docs/api/puppeteer.frame.md @@ -75,7 +75,6 @@ console.log(text); | [$$(selector)](./puppeteer.frame.__.md) | | Queries the frame for all elements matching the given selector. | | [$$eval(selector, pageFunction, args)](./puppeteer.frame.__eval.md) | |

Runs the given function on an array of elements matching the given selector in the frame.

If the given function returns a promise, then this method will wait till the promise resolves.

| | [$eval(selector, pageFunction, args)](./puppeteer.frame._eval.md) | |

Runs the given function on the first element matching the given selector in the frame.

If the given function returns a promise, then this method will wait till the promise resolves.

| -| [$x(expression)](./puppeteer.frame._x.md) | | | | [addScriptTag(options)](./puppeteer.frame.addscripttag.md) | | Adds a <script> tag into the page with the desired url or content. | | [addStyleTag(options)](./puppeteer.frame.addstyletag.md) | | Adds a HTMLStyleElement into the frame with the desired URL | | [addStyleTag(options)](./puppeteer.frame.addstyletag_1.md) | | Adds a HTMLLinkElement into the frame with the desired URL | @@ -103,4 +102,3 @@ console.log(text); | [waitForFunction(pageFunction, options, args)](./puppeteer.frame.waitforfunction.md) | | | | [waitForNavigation(options)](./puppeteer.frame.waitfornavigation.md) | |

Waits for the frame to navigate. It is useful for when you run code which will indirectly cause the frame to navigate.

Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL is considered a navigation.

| | [waitForSelector(selector, options)](./puppeteer.frame.waitforselector.md) | |

Waits for an element matching the given selector to appear in the frame.

This method works across navigations.

| -| [waitForXPath(xpath, options)](./puppeteer.frame.waitforxpath.md) | | | diff --git a/docs/api/puppeteer.frame.waitforxpath.md b/docs/api/puppeteer.frame.waitforxpath.md deleted file mode 100644 index 6a98e2153f6..00000000000 --- a/docs/api/puppeteer.frame.waitforxpath.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -sidebar_label: Frame.waitForXPath ---- - -# Frame.waitForXPath() method - -> Warning: This API is now obsolete. -> -> Use [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md) with the `xpath` prefix. -> -> Example: `await frame.waitForSelector('xpath/' + xpathExpression)` -> -> The method evaluates the XPath expression relative to the Frame. If `xpath` starts with `//` instead of `.//`, the dot will be appended automatically. -> -> 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. -> -> For a code example, see the example for [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md). That function behaves identically other than taking a CSS selector rather than an XPath. - -#### Signature: - -```typescript -class Frame { - waitForXPath( - xpath: string, - options?: WaitForSelectorOptions - ): Promise | null>; -} -``` - -## Parameters - -| Parameter | Type | Description | -| --------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | -| xpath | string | the XPath expression to wait for. | -| options | [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | _(Optional)_ options to configure the visibility of the element and how long to wait before timing out. | - -**Returns:** - -Promise<[ElementHandle](./puppeteer.elementhandle.md)<Node> \| null> diff --git a/docs/api/puppeteer.page._x.md b/docs/api/puppeteer.page._x.md deleted file mode 100644 index 4acba3b5348..00000000000 --- a/docs/api/puppeteer.page._x.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -sidebar_label: Page.$x ---- - -# Page.$x() method - -The method evaluates the XPath expression relative to the page document as its context node. If there are no such elements, the method resolves to an empty array. - -#### Signature: - -```typescript -class Page { - $x(expression: string): Promise>>; -} -``` - -## Parameters - -| Parameter | Type | Description | -| ---------- | ------ | ---------------------- | -| expression | string | Expression to evaluate | - -**Returns:** - -Promise<Array<[ElementHandle](./puppeteer.elementhandle.md)<Node>>> - -## Remarks - -Shortcut for [Page.mainFrame().$x(expression)](./puppeteer.frame._x.md). diff --git a/docs/api/puppeteer.page.md b/docs/api/puppeteer.page.md index a2c4242dc0b..07903339b1d 100644 --- a/docs/api/puppeteer.page.md +++ b/docs/api/puppeteer.page.md @@ -80,7 +80,6 @@ page.off('request', logRequest); | [$$(selector)](./puppeteer.page.__.md) | | The method runs document.querySelectorAll within the page. If no elements match the selector, the return value resolves to []. | | [$$eval(selector, pageFunction, args)](./puppeteer.page.__eval.md) | | This method runs Array.from(document.querySelectorAll(selector)) within the page and passes the result as the first argument to the pageFunction. | | [$eval(selector, pageFunction, args)](./puppeteer.page._eval.md) | | This method runs document.querySelector within the page and passes the result as the first argument to the pageFunction. | -| [$x(expression)](./puppeteer.page._x.md) | | The method evaluates the XPath expression relative to the page document as its context node. If there are no such elements, the method resolves to an empty array. | | [addScriptTag(options)](./puppeteer.page.addscripttag.md) | | Adds a <script> tag into the page with the desired URL or content. | | [addStyleTag(options)](./puppeteer.page.addstyletag.md) | |

Adds a <link rel="stylesheet"> tag into the page with the desired URL or a <style type="text/css"> tag with the content.

Shortcut for [page.mainFrame().addStyleTag(options)](./puppeteer.frame.addstyletag_1.md).

| | [addStyleTag(options)](./puppeteer.page.addstyletag_1.md) | | | @@ -161,5 +160,4 @@ page.off('request', logRequest); | [waitForRequest(urlOrPredicate, options)](./puppeteer.page.waitforrequest.md) | | | | [waitForResponse(urlOrPredicate, options)](./puppeteer.page.waitforresponse.md) | | | | [waitForSelector(selector, options)](./puppeteer.page.waitforselector.md) | | Wait for the selector to appear in page. If at the moment of calling the method the selector already exists, the method will return immediately. If the selector doesn't appear after the timeout milliseconds of waiting, the function will throw. | -| [waitForXPath(xpath, options)](./puppeteer.page.waitforxpath.md) | | 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. | | [workers()](./puppeteer.page.workers.md) | | All of the dedicated [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) associated with the page. | diff --git a/docs/api/puppeteer.page.waitforxpath.md b/docs/api/puppeteer.page.waitforxpath.md deleted file mode 100644 index 7f8aaa39ac3..00000000000 --- a/docs/api/puppeteer.page.waitforxpath.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -sidebar_label: Page.waitForXPath ---- - -# Page.waitForXPath() method - -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. - -#### Signature: - -```typescript -class Page { - waitForXPath( - xpath: string, - options?: WaitForSelectorOptions - ): Promise | null>; -} -``` - -## Parameters - -| Parameter | Type | Description | -| --------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| xpath | string | A [xpath](https://developer.mozilla.org/en-US/docs/Web/XPath) of an element to wait for | -| options | [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | _(Optional)_ Optional waiting parameters | - -**Returns:** - -Promise<[ElementHandle](./puppeteer.elementhandle.md)<Node> \| null> - -Promise which resolves when element specified by xpath string is added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is not found in DOM, otherwise resolves to `ElementHandle`. - -## Remarks - -The optional Argument `options` have properties: - -- `visible`: A boolean to 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`: A 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`: A number which is maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md) method. - -## Example - -This method works across navigation - -```ts -import puppeteer from 'puppeteer'; -(async () => { - const browser = await puppeteer.launch(); - 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(); -})(); -``` diff --git a/packages/puppeteer-core/src/api/ElementHandle.ts b/packages/puppeteer-core/src/api/ElementHandle.ts index cb3868341d9..9b1326f998e 100644 --- a/packages/puppeteer-core/src/api/ElementHandle.ts +++ b/packages/puppeteer-core/src/api/ElementHandle.ts @@ -476,27 +476,6 @@ export abstract class ElementHandle< return result; } - /** - * @deprecated Use {@link ElementHandle.$$} with the `xpath` prefix. - * - * Example: `await elementHandle.$$('xpath/' + xpathExpression)` - * - * The method evaluates the XPath expression relative to the elementHandle. - * If `xpath` starts with `//` instead of `.//`, the dot will be appended - * automatically. - * - * If there are no such elements, the method will resolve to an empty array. - * @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate} - */ - @throwIfDisposed() - @ElementHandle.bindIsolatedHandle - async $x(expression: string): Promise>> { - if (expression.startsWith('//')) { - expression = `.${expression}`; - } - return await this.$$(`xpath/${expression}`); - } - /** * Wait for an element matching the given selector to appear in the current * element. @@ -581,84 +560,6 @@ export abstract class ElementHandle< return await this.#checkVisibility(false); } - /** - * @deprecated Use {@link ElementHandle.waitForSelector} with the `xpath` - * prefix. - * - * Example: `await elementHandle.waitForSelector('xpath/' + xpathExpression)` - * - * The method evaluates the XPath expression relative to the elementHandle. - * - * Wait for the `xpath` within the element. 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. - * - * If `xpath` starts with `//` instead of `.//`, the dot will be appended - * automatically. - * - * @example - * This method works across navigation. - * - * ```ts - * import puppeteer from 'puppeteer'; - * (async () => { - * const browser = await puppeteer.launch(); - * 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(); - * })(); - * ``` - * - * @param xpath - A - * {@link https://developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an - * element to wait for - * @param options - Optional waiting parameters - * @returns Promise which resolves when element specified by xpath string is - * added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is - * not found in DOM, otherwise resolves to `ElementHandle`. - * @remarks - * The optional Argument `options` have properties: - * - * - `visible`: A boolean to 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`: A 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`: A number which is maximum time to wait for in milliseconds. - * Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The - * default value can be changed by using the {@link Page.setDefaultTimeout} - * method. - */ - @throwIfDisposed() - @ElementHandle.bindIsolatedHandle - async waitForXPath( - xpath: string, - options: { - visible?: boolean; - hidden?: boolean; - timeout?: number; - } = {} - ): Promise | null> { - if (xpath.startsWith('//')) { - xpath = `.${xpath}`; - } - return await this.waitForSelector(`xpath/${xpath}`, options); - } - /** * Converts the current handle to the given element type. * diff --git a/packages/puppeteer-core/src/api/Frame.ts b/packages/puppeteer-core/src/api/Frame.ts index 99065b12a4e..7cd98d518d4 100644 --- a/packages/puppeteer-core/src/api/Frame.ts +++ b/packages/puppeteer-core/src/api/Frame.ts @@ -623,23 +623,6 @@ export abstract class Frame extends EventEmitter { return await document.$$eval(selector, pageFunction, ...args); } - /** - * @deprecated Use {@link Frame.$$} with the `xpath` prefix. - * - * Example: `await frame.$$('xpath/' + xpathExpression)` - * - * This method evaluates the given XPath expression and returns the results. - * If `xpath` starts with `//` instead of `.//`, the dot will be appended - * automatically. - * @param expression - the XPath expression to evaluate. - */ - @throwIfDetached - async $x(expression: string): Promise>> { - // eslint-disable-next-line rulesdir/use-using -- This is cached. - const document = await this.#document(); - return await document.$x(expression); - } - /** * Waits for an element matching the given selector to appear in the frame. * @@ -689,39 +672,6 @@ export abstract class Frame extends EventEmitter { )) as ElementHandle> | null; } - /** - * @deprecated Use {@link Frame.waitForSelector} with the `xpath` prefix. - * - * Example: `await frame.waitForSelector('xpath/' + xpathExpression)` - * - * The method evaluates the XPath expression relative to the Frame. - * If `xpath` starts with `//` instead of `.//`, the dot will be appended - * automatically. - * - * 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. - * - * For a code example, see the example for {@link Frame.waitForSelector}. That - * function behaves identically other than taking a CSS selector rather than - * an XPath. - * - * @param xpath - the XPath expression to wait for. - * @param options - options to configure the visibility of the element and how - * long to wait before timing out. - */ - @throwIfDetached - async waitForXPath( - xpath: string, - options: WaitForSelectorOptions = {} - ): Promise | null> { - if (xpath.startsWith('//')) { - xpath = `.${xpath}`; - } - return await this.waitForSelector(`xpath/${xpath}`, options); - } - /** * @example * The `waitForFunction` can be used to observe viewport size change: diff --git a/packages/puppeteer-core/src/api/Page.ts b/packages/puppeteer-core/src/api/Page.ts index 70985b93543..b2682ae92ff 100644 --- a/packages/puppeteer-core/src/api/Page.ts +++ b/packages/puppeteer-core/src/api/Page.ts @@ -1290,20 +1290,6 @@ export abstract class Page extends EventEmitter { return await this.mainFrame().$$eval(selector, pageFunction, ...args); } - /** - * The method evaluates the XPath expression relative to the page document as - * its context node. If there are no such elements, the method resolves to an - * empty array. - * - * @remarks - * Shortcut for {@link Frame.$x | Page.mainFrame().$x(expression) }. - * - * @param expression - Expression to evaluate - */ - async $x(expression: string): Promise>> { - return await this.mainFrame().$x(expression); - } - /** * If no URLs are specified, this method returns cookies for the current page * URL. If URLs are specified, only cookies for those URLs are returned. @@ -2843,64 +2829,6 @@ export abstract class Page extends EventEmitter { return await this.mainFrame().waitForSelector(selector, options); } - /** - * 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. - * - * @example - * This method works across navigation - * - * ```ts - * import puppeteer from 'puppeteer'; - * (async () => { - * const browser = await puppeteer.launch(); - * 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(); - * })(); - * ``` - * - * @param xpath - A - * {@link https://developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an - * element to wait for - * @param options - Optional waiting parameters - * @returns Promise which resolves when element specified by xpath string is - * added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is - * not found in DOM, otherwise resolves to `ElementHandle`. - * @remarks - * The optional Argument `options` have properties: - * - * - `visible`: A boolean to 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`: A 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`: A number which is maximum time to wait for in milliseconds. - * Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default - * value can be changed by using the {@link Page.setDefaultTimeout} method. - */ - waitForXPath( - xpath: string, - options?: WaitForSelectorOptions - ): Promise | null> { - return this.mainFrame().waitForXPath(xpath, options); - } - /** * Waits for the provided function, `pageFunction`, to return a truthy value when * evaluated in the page's context. diff --git a/test/TestExpectations.json b/test/TestExpectations.json index 8ddac7c9197..d103a3e8a0b 100644 --- a/test/TestExpectations.json +++ b/test/TestExpectations.json @@ -3704,25 +3704,25 @@ "expectations": ["SKIP"] }, { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame", + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector xpath should run in specified frame", "platforms": ["darwin", "linux", "win32"], "parameters": ["firefox", "webDriverBiDi"], "expectations": ["PASS"] }, { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should run in specified frame", + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector xpath should run in specified frame", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] }, { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached", + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector xpath should throw when frame is detached", "platforms": ["darwin", "linux", "win32"], "parameters": ["firefox", "webDriverBiDi"], "expectations": ["PASS"] }, { - "testIdPattern": "[waittask.spec] waittask specs Frame.waitForXPath should throw when frame is detached", + "testIdPattern": "[waittask.spec] waittask specs Frame.waitForSelector xpath should throw when frame is detached", "platforms": ["darwin", "linux", "win32"], "parameters": ["cdp", "firefox"], "expectations": ["SKIP"] diff --git a/test/src/elementhandle.spec.ts b/test/src/elementhandle.spec.ts index 9aaf9142247..9a011ce3e76 100644 --- a/test/src/elementhandle.spec.ts +++ b/test/src/elementhandle.spec.ts @@ -472,10 +472,8 @@ describe('ElementHandle specs', function () { }) ).toStrictEqual('bar1'); }); - }); - describe('Element.waitForXPath', () => { - it('should wait correctly with waitForXPath on an element', async () => { + it('should wait correctly with waitForSelector and xpath on an element', async () => { const {page} = await getTestState(); // Set the page content after the waitFor has been started. await page.setContent( @@ -490,20 +488,18 @@ describe('ElementHandle specs', function () { ` ); - using el1 = (await page.waitForSelector( + using elById = (await page.waitForSelector( '#el1' )) as ElementHandle; - for (const path of ['//div', './/div']) { - using e = (await el1.waitForXPath( - path - )) as ElementHandle; - expect( - await e.evaluate(el => { - return el.id; - }) - ).toStrictEqual('el2'); - } + using elByXpath = (await elById.waitForSelector( + 'xpath/.//div' + )) as ElementHandle; + expect( + await elByXpath.evaluate(el => { + return el.id; + }) + ).toStrictEqual('el2'); }); }); diff --git a/test/src/queryselector.spec.ts b/test/src/queryselector.spec.ts index 7fd27f914f0..c8df118e5f4 100644 --- a/test/src/queryselector.spec.ts +++ b/test/src/queryselector.spec.ts @@ -174,29 +174,29 @@ describe('querySelector', function () { const elements = await page.$$('div'); expect(elements).toHaveLength(0); }); - }); - describe('Page.$x', function () { - it('should query existing element', async () => { - const {page} = await getTestState(); + describe('xpath', function () { + it('should query existing element', async () => { + const {page} = await getTestState(); - await page.setContent('
test
'); - const elements = await page.$x('/html/body/section'); - expect(elements[0]).toBeTruthy(); - expect(elements).toHaveLength(1); - }); - it('should return empty array for non-existing element', async () => { - const {page} = await getTestState(); + await page.setContent('
test
'); + const elements = await page.$$('xpath/html/body/section'); + expect(elements[0]).toBeTruthy(); + expect(elements).toHaveLength(1); + }); + it('should return empty array for non-existing element', async () => { + const {page} = await getTestState(); - const element = await page.$x('/html/body/non-existing-element'); - expect(element).toEqual([]); - }); - it('should return multiple elements', async () => { - const {page} = await getTestState(); + const element = await page.$$('xpath/html/body/non-existing-element'); + expect(element).toEqual([]); + }); + it('should return multiple elements', async () => { + const {page} = await getTestState(); - await page.setContent('
'); - const elements = await page.$x('/html/body/div'); - expect(elements).toHaveLength(2); + await page.setContent('
'); + const elements = await page.$$('xpath/html/body/div'); + expect(elements).toHaveLength(2); + }); }); }); @@ -347,37 +347,40 @@ describe('querySelector', function () { const elements = await html.$$('div'); expect(elements).toHaveLength(0); }); - }); - describe('ElementHandle.$x', function () { - it('should query existing element', async () => { - const {page, server} = await getTestState(); + describe('xpath', function () { + it('should query existing element', async () => { + const {page, server} = await getTestState(); - await page.goto(server.PREFIX + '/playground.html'); - await page.setContent( - '
A
' - ); - using html = (await page.$('html'))!; - const second = await html.$x(`./body/div[contains(@class, 'second')]`); - const inner = await second[0]!.$x(`./div[contains(@class, 'inner')]`); - const content = await page.evaluate(e => { - return e.textContent; - }, inner[0]!); - expect(content).toBe('A'); - }); + await page.goto(server.PREFIX + '/playground.html'); + await page.setContent( + '
A
' + ); + using html = (await page.$('html'))!; + const second = await html.$$( + `xpath/./body/div[contains(@class, 'second')]` + ); + const inner = await second[0]!.$$( + `xpath/./div[contains(@class, 'inner')]` + ); + const content = await page.evaluate(e => { + return e.textContent; + }, inner[0]!); + expect(content).toBe('A'); + }); - it('should return null for non-existing element', async () => { - const {page} = await getTestState(); + it('should return null for non-existing element', async () => { + const {page} = await getTestState(); - await page.setContent( - '
B
' - ); - using html = (await page.$('html'))!; - const second = await html.$x(`/div[contains(@class, 'third')]`); - expect(second).toEqual([]); + await page.setContent( + '
B
' + ); + using html = (await page.$('html'))!; + const second = await html.$$(`xpath/div[contains(@class, 'third')]`); + expect(second).toEqual([]); + }); }); }); - // This is the same tests for `$$eval` and `$$` as above, but with a queryAll // handler that returns an array instead of a list of nodes. describe('QueryAll', function () { diff --git a/test/src/waittask.spec.ts b/test/src/waittask.spec.ts index 97aefa676e0..dd8c55c0bee 100644 --- a/test/src/waittask.spec.ts +++ b/test/src/waittask.spec.ts @@ -693,142 +693,150 @@ describe('waittask specs', function () { // The extension is ts here as Mocha maps back via sourcemaps. expect(error?.stack).toContain('WaitTask.ts'); }); - }); - describe('Frame.waitForXPath', function () { - const addElement = (tag: string) => { - return document.body.appendChild(document.createElement(tag)); - }; + describe('xpath', function () { + const addElement = (tag: string) => { + return document.body.appendChild(document.createElement(tag)); + }; - it('should support some fancy xpath', async () => { - const {page} = await getTestState(); + it('should support some fancy xpath', async () => { + const {page} = await getTestState(); - await page.setContent(`

red herring

hello world

`); - const waitForXPath = page.waitForXPath( - '//p[normalize-space(.)="hello world"]' - ); - expect( - await page.evaluate( - x => { - return x?.textContent; - }, - await waitForXPath - ) - ).toBe('hello world '); - }); - it('should respect timeout', async () => { - const {page} = await getTestState(); - - let error!: Error; - await page.waitForXPath('//div', {timeout: 10}).catch(error_ => { - return (error = error_); + await page.setContent(`

red herring

hello world

`); + const waitForSelector = page.waitForSelector( + 'xpath/.//p[normalize-space(.)="hello world"]' + ); + expect( + await page.evaluate( + x => { + return x?.textContent; + }, + await waitForSelector + ) + ).toBe('hello world '); }); - expect(error).toBeInstanceOf(TimeoutError); - expect(error?.message).toContain('Waiting failed: 10ms exceeded'); - }); - it('should run in specified frame', async () => { - const {page, server} = await getTestState(); + it('should respect timeout', async () => { + const {page} = await getTestState(); - await attachFrame(page, 'frame1', server.EMPTY_PAGE); - await 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'); - using eHandle = await waitForXPathPromise; - expect(eHandle?.frame).toBe(frame2); - }); - it('should throw when frame is detached', async () => { - const {page, server} = await getTestState(); - - await attachFrame(page, 'frame1', server.EMPTY_PAGE); - const frame = page.frames()[1]!; - let waitError: Error | undefined; - const waitPromise = frame - .waitForXPath('//*[@class="box"]') - .catch(error => { - return (waitError = error); - }); - await 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 () => { - const {page} = await getTestState(); - - let divHidden = false; - await page.setContent(`
text
`); - const waitForXPath = page - .waitForXPath('//div', {hidden: true}) - .then(() => { - return (divHidden = true); - }); - await page.waitForXPath('//div'); // do a round trip - expect(divHidden).toBe(false); - await page.evaluate(() => { - return document - .querySelector('div') - ?.style.setProperty('display', 'none'); + let error!: Error; + await page + .waitForSelector('xpath/.//div', {timeout: 10}) + .catch(error_ => { + return (error = error_); + }); + expect(error).toBeInstanceOf(TimeoutError); + expect(error?.message).toContain('Waiting failed: 10ms exceeded'); }); - expect(await waitForXPath).toBe(true); - expect(divHidden).toBe(true); - }); - it('hidden should return null if the element is not found', async () => { - const {page} = await getTestState(); + it('should run in specified frame', async () => { + const {page, server} = await getTestState(); - using waitForXPath = await page.waitForXPath('//div', {hidden: true}); + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + await attachFrame(page, 'frame2', server.EMPTY_PAGE); + const frame1 = page.frames()[1]!; + const frame2 = page.frames()[2]!; + const waitForSelector = frame2.waitForSelector('xpath/.//div'); + await frame1.evaluate(addElement, 'div'); + await frame2.evaluate(addElement, 'div'); + using eHandle = await waitForSelector; + expect(eHandle?.frame).toBe(frame2); + }); + it('should throw when frame is detached', async () => { + const {page, server} = await getTestState(); - expect(waitForXPath).toBe(null); - }); - it('hidden should return an empty element handle if the element is found', async () => { - const {page} = await getTestState(); + await attachFrame(page, 'frame1', server.EMPTY_PAGE); + const frame = page.frames()[1]!; + let waitError: Error | undefined; + const waitPromise = frame + .waitForSelector('xpath/.//*[@class="box"]') + .catch(error => { + return (waitError = error); + }); + await 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 () => { + const {page} = await getTestState(); - await page.setContent(`
text
`); + let divHidden = false; + await page.setContent(`
text
`); + const waitForSelector = page + .waitForSelector('xpath/.//div', {hidden: true}) + .then(() => { + return (divHidden = true); + }); + await page.waitForSelector('xpath/.//div'); // do a round trip + expect(divHidden).toBe(false); + await page.evaluate(() => { + return document + .querySelector('div') + ?.style.setProperty('display', 'none'); + }); + expect(await waitForSelector).toBe(true); + expect(divHidden).toBe(true); + }); + it('hidden should return null if the element is not found', async () => { + const {page} = await getTestState(); - using waitForXPath = await page.waitForXPath('//div', {hidden: true}); + using waitForSelector = await page.waitForSelector('xpath/.//div', { + hidden: true, + }); - expect(waitForXPath).toBeInstanceOf(ElementHandle); - }); - it('should return the element handle', async () => { - const {page} = await getTestState(); + expect(waitForSelector).toBe(null); + }); + it('hidden should return an empty element handle if the element is found', async () => { + const {page} = await getTestState(); - const waitForXPath = page.waitForXPath('//*[@class="zombo"]'); - await page.setContent(`
anything
`); - expect( - await page.evaluate( - x => { - return x?.textContent; - }, - await waitForXPath - ) - ).toBe('anything'); - }); - it('should allow you to select a text node', async () => { - const {page} = await getTestState(); + await page.setContent(`
text
`); - await page.setContent(`
some text
`); - using 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 () => { - const {page} = await getTestState(); + using waitForSelector = await page.waitForSelector('xpath/.//div', { + hidden: true, + }); - await page.setContent(`
some text
`); - const waitForXPath = page.waitForXPath('/html/body/div'); - expect( - await page.evaluate( - x => { - return x?.textContent; - }, - await waitForXPath - ) - ).toBe('some text'); + expect(waitForSelector).toBeInstanceOf(ElementHandle); + }); + it('should return the element handle', async () => { + const {page} = await getTestState(); + + const waitForSelector = page.waitForSelector( + 'xpath/.//*[@class="zombo"]' + ); + await page.setContent(`
anything
`); + expect( + await page.evaluate( + x => { + return x?.textContent; + }, + await waitForSelector + ) + ).toBe('anything'); + }); + it('should allow you to select a text node', async () => { + const {page} = await getTestState(); + + await page.setContent(`
some text
`); + using text = await page.waitForSelector('xpath/.//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 () => { + const {page} = await getTestState(); + + await page.setContent(`
some text
`); + const waitForSelector = page.waitForSelector('xpath/html/body/div'); + expect( + await page.evaluate( + x => { + return x?.textContent; + }, + await waitForSelector + ) + ).toBe('some text'); + }); }); }); }); diff --git a/website/versioned_docs/version-21.11.0/api/puppeteer.frame.waitfortimeout.md b/website/versioned_docs/version-21.11.0/api/puppeteer.frame.waitfortimeout.md index fd3a2339086..59ae8e82c2b 100644 --- a/website/versioned_docs/version-21.11.0/api/puppeteer.frame.waitfortimeout.md +++ b/website/versioned_docs/version-21.11.0/api/puppeteer.frame.waitfortimeout.md @@ -30,7 +30,7 @@ Promise<void> ## Remarks -It's generally recommended to not wait for a number of seconds, but instead use [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), [Frame.waitForXPath()](./puppeteer.frame.waitforxpath.md) or [Frame.waitForFunction()](./puppeteer.frame.waitforfunction.md) to wait for exactly the conditions you want. +It's generally recommended to not wait for a number of seconds, but instead use [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), or [Frame.waitForFunction()](./puppeteer.frame.waitforfunction.md) to wait for exactly the conditions you want. ## Example diff --git a/website/versioned_docs/version-21.11.0/api/puppeteer.page.waitfortimeout.md b/website/versioned_docs/version-21.11.0/api/puppeteer.page.waitfortimeout.md index 681bf8a7249..0a51b594d80 100644 --- a/website/versioned_docs/version-21.11.0/api/puppeteer.page.waitfortimeout.md +++ b/website/versioned_docs/version-21.11.0/api/puppeteer.page.waitfortimeout.md @@ -30,7 +30,7 @@ Promise<void> ## Remarks -It's generally recommended to not wait for a number of seconds, but instead use [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), [Frame.waitForXPath()](./puppeteer.frame.waitforxpath.md) or [Frame.waitForFunction()](./puppeteer.frame.waitforfunction.md) to wait for exactly the conditions you want. +It's generally recommended to not wait for a number of seconds, but instead use [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), or [Frame.waitForFunction()](./puppeteer.frame.waitforfunction.md) to wait for exactly the conditions you want. ## Example