mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: use an xpath
query handler (#8730)
This commit is contained in:
parent
49193cbf1c
commit
5cf9b4de8d
@ -61,63 +61,63 @@ sidebar_label: API
|
||||
|
||||
## Interfaces
|
||||
|
||||
| Interface | Description |
|
||||
| --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [BoundingBox](./puppeteer.boundingbox.md) | |
|
||||
| [BoxModel](./puppeteer.boxmodel.md) | |
|
||||
| [BrowserConnectOptions](./puppeteer.browserconnectoptions.md) | Generic browser options that can be passed when launching any browser or when connecting to an existing browser instance. |
|
||||
| [BrowserContextOptions](./puppeteer.browsercontextoptions.md) | BrowserContext options. |
|
||||
| [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | |
|
||||
| [BrowserFetcherRevisionInfo](./puppeteer.browserfetcherrevisioninfo.md) | |
|
||||
| [BrowserLaunchArgumentOptions](./puppeteer.browserlaunchargumentoptions.md) | Launcher options that only apply to Chrome. |
|
||||
| [CDPSessionOnMessageObject](./puppeteer.cdpsessiononmessageobject.md) | |
|
||||
| [ClickOptions](./puppeteer.clickoptions.md) | |
|
||||
| [CommonEventEmitter](./puppeteer.commoneventemitter.md) | |
|
||||
| [ConnectionCallback](./puppeteer.connectioncallback.md) | |
|
||||
| [ConnectionTransport](./puppeteer.connectiontransport.md) | |
|
||||
| [ConnectOptions](./puppeteer.connectoptions.md) | |
|
||||
| [ConsoleMessageLocation](./puppeteer.consolemessagelocation.md) | |
|
||||
| [ContinueRequestOverrides](./puppeteer.continuerequestoverrides.md) | |
|
||||
| [CoverageEntry](./puppeteer.coverageentry.md) | The CoverageEntry class represents one entry of the coverage report. |
|
||||
| [Credentials](./puppeteer.credentials.md) | |
|
||||
| [CSSCoverageOptions](./puppeteer.csscoverageoptions.md) | Set of configurable options for CSS coverage. |
|
||||
| [CustomQueryHandler](./puppeteer.customqueryhandler.md) | Contains two functions <code>queryOne</code> and <code>queryAll</code> that can be [registered](./puppeteer.registercustomqueryhandler.md) as alternative querying strategies. The functions <code>queryOne</code> and <code>queryAll</code> are executed in the page context. <code>queryOne</code> should take an <code>Element</code> and a selector string as argument and return a single <code>Element</code> or <code>null</code> if no element is found. <code>queryAll</code> takes the same arguments but should instead return a <code>NodeListOf<Element></code> or <code>Array<Element></code> with all the elements that match the given query selector. |
|
||||
| [Device](./puppeteer.device.md) | |
|
||||
| [FrameAddScriptTagOptions](./puppeteer.frameaddscripttagoptions.md) | |
|
||||
| [FrameAddStyleTagOptions](./puppeteer.frameaddstyletagoptions.md) | |
|
||||
| [FrameWaitForFunctionOptions](./puppeteer.framewaitforfunctionoptions.md) | |
|
||||
| [GeolocationOptions](./puppeteer.geolocationoptions.md) | |
|
||||
| [InterceptResolutionState](./puppeteer.interceptresolutionstate.md) | |
|
||||
| [InternalNetworkConditions](./puppeteer.internalnetworkconditions.md) | |
|
||||
| [JSCoverageEntry](./puppeteer.jscoverageentry.md) | The CoverageEntry class for JavaScript |
|
||||
| [JSCoverageOptions](./puppeteer.jscoverageoptions.md) | Set of configurable options for JS coverage. |
|
||||
| [LaunchOptions](./puppeteer.launchoptions.md) | Generic launch options that can be passed when launching any browser. |
|
||||
| [MediaFeature](./puppeteer.mediafeature.md) | |
|
||||
| [Metrics](./puppeteer.metrics.md) | |
|
||||
| [MouseOptions](./puppeteer.mouseoptions.md) | |
|
||||
| [MouseWheelOptions](./puppeteer.mousewheeloptions.md) | |
|
||||
| [NetworkConditions](./puppeteer.networkconditions.md) | |
|
||||
| [Offset](./puppeteer.offset.md) | |
|
||||
| [PageEventObject](./puppeteer.pageeventobject.md) | <p>Denotes the objects received by callback functions for page events.</p><p>See [PageEmittedEvents](./puppeteer.pageemittedevents.md) for more detail on the events and when they are emitted.</p> |
|
||||
| [PDFMargin](./puppeteer.pdfmargin.md) | |
|
||||
| [PDFOptions](./puppeteer.pdfoptions.md) | Valid options to configure PDF generation via [Page.pdf()](./puppeteer.page.pdf.md). |
|
||||
| [Point](./puppeteer.point.md) | |
|
||||
| [PressOptions](./puppeteer.pressoptions.md) | |
|
||||
| [ProductLauncher](./puppeteer.productlauncher.md) | Describes a launcher - a class that is able to create and launch a browser instance. |
|
||||
| [PuppeteerErrors](./puppeteer.puppeteererrors.md) | |
|
||||
| [PuppeteerLaunchOptions](./puppeteer.puppeteerlaunchoptions.md) | |
|
||||
| [RemoteAddress](./puppeteer.remoteaddress.md) | |
|
||||
| [ResponseForRequest](./puppeteer.responseforrequest.md) | Required response data to fulfill a request with. |
|
||||
| [ScreenshotClip](./puppeteer.screenshotclip.md) | |
|
||||
| [ScreenshotOptions](./puppeteer.screenshotoptions.md) | |
|
||||
| [SerializedAXNode](./puppeteer.serializedaxnode.md) | Represents a Node and the properties of it that are relevant to Accessibility. |
|
||||
| [SnapshotOptions](./puppeteer.snapshotoptions.md) | |
|
||||
| [TracingOptions](./puppeteer.tracingoptions.md) | |
|
||||
| [Viewport](./puppeteer.viewport.md) | Sets the viewport of the page. |
|
||||
| [WaitForOptions](./puppeteer.waitforoptions.md) | |
|
||||
| [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | |
|
||||
| [WaitForTargetOptions](./puppeteer.waitfortargetoptions.md) | |
|
||||
| [WaitTimeoutOptions](./puppeteer.waittimeoutoptions.md) | |
|
||||
| Interface | Description |
|
||||
| --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [BoundingBox](./puppeteer.boundingbox.md) | |
|
||||
| [BoxModel](./puppeteer.boxmodel.md) | |
|
||||
| [BrowserConnectOptions](./puppeteer.browserconnectoptions.md) | Generic browser options that can be passed when launching any browser or when connecting to an existing browser instance. |
|
||||
| [BrowserContextOptions](./puppeteer.browsercontextoptions.md) | BrowserContext options. |
|
||||
| [BrowserFetcherOptions](./puppeteer.browserfetcheroptions.md) | |
|
||||
| [BrowserFetcherRevisionInfo](./puppeteer.browserfetcherrevisioninfo.md) | |
|
||||
| [BrowserLaunchArgumentOptions](./puppeteer.browserlaunchargumentoptions.md) | Launcher options that only apply to Chrome. |
|
||||
| [CDPSessionOnMessageObject](./puppeteer.cdpsessiononmessageobject.md) | |
|
||||
| [ClickOptions](./puppeteer.clickoptions.md) | |
|
||||
| [CommonEventEmitter](./puppeteer.commoneventemitter.md) | |
|
||||
| [ConnectionCallback](./puppeteer.connectioncallback.md) | |
|
||||
| [ConnectionTransport](./puppeteer.connectiontransport.md) | |
|
||||
| [ConnectOptions](./puppeteer.connectoptions.md) | |
|
||||
| [ConsoleMessageLocation](./puppeteer.consolemessagelocation.md) | |
|
||||
| [ContinueRequestOverrides](./puppeteer.continuerequestoverrides.md) | |
|
||||
| [CoverageEntry](./puppeteer.coverageentry.md) | The CoverageEntry class represents one entry of the coverage report. |
|
||||
| [Credentials](./puppeteer.credentials.md) | |
|
||||
| [CSSCoverageOptions](./puppeteer.csscoverageoptions.md) | Set of configurable options for CSS coverage. |
|
||||
| [CustomQueryHandler](./puppeteer.customqueryhandler.md) | |
|
||||
| [Device](./puppeteer.device.md) | |
|
||||
| [FrameAddScriptTagOptions](./puppeteer.frameaddscripttagoptions.md) | |
|
||||
| [FrameAddStyleTagOptions](./puppeteer.frameaddstyletagoptions.md) | |
|
||||
| [FrameWaitForFunctionOptions](./puppeteer.framewaitforfunctionoptions.md) | |
|
||||
| [GeolocationOptions](./puppeteer.geolocationoptions.md) | |
|
||||
| [InterceptResolutionState](./puppeteer.interceptresolutionstate.md) | |
|
||||
| [InternalNetworkConditions](./puppeteer.internalnetworkconditions.md) | |
|
||||
| [JSCoverageEntry](./puppeteer.jscoverageentry.md) | The CoverageEntry class for JavaScript |
|
||||
| [JSCoverageOptions](./puppeteer.jscoverageoptions.md) | Set of configurable options for JS coverage. |
|
||||
| [LaunchOptions](./puppeteer.launchoptions.md) | Generic launch options that can be passed when launching any browser. |
|
||||
| [MediaFeature](./puppeteer.mediafeature.md) | |
|
||||
| [Metrics](./puppeteer.metrics.md) | |
|
||||
| [MouseOptions](./puppeteer.mouseoptions.md) | |
|
||||
| [MouseWheelOptions](./puppeteer.mousewheeloptions.md) | |
|
||||
| [NetworkConditions](./puppeteer.networkconditions.md) | |
|
||||
| [Offset](./puppeteer.offset.md) | |
|
||||
| [PageEventObject](./puppeteer.pageeventobject.md) | <p>Denotes the objects received by callback functions for page events.</p><p>See [PageEmittedEvents](./puppeteer.pageemittedevents.md) for more detail on the events and when they are emitted.</p> |
|
||||
| [PDFMargin](./puppeteer.pdfmargin.md) | |
|
||||
| [PDFOptions](./puppeteer.pdfoptions.md) | Valid options to configure PDF generation via [Page.pdf()](./puppeteer.page.pdf.md). |
|
||||
| [Point](./puppeteer.point.md) | |
|
||||
| [PressOptions](./puppeteer.pressoptions.md) | |
|
||||
| [ProductLauncher](./puppeteer.productlauncher.md) | Describes a launcher - a class that is able to create and launch a browser instance. |
|
||||
| [PuppeteerErrors](./puppeteer.puppeteererrors.md) | |
|
||||
| [PuppeteerLaunchOptions](./puppeteer.puppeteerlaunchoptions.md) | |
|
||||
| [RemoteAddress](./puppeteer.remoteaddress.md) | |
|
||||
| [ResponseForRequest](./puppeteer.responseforrequest.md) | Required response data to fulfill a request with. |
|
||||
| [ScreenshotClip](./puppeteer.screenshotclip.md) | |
|
||||
| [ScreenshotOptions](./puppeteer.screenshotoptions.md) | |
|
||||
| [SerializedAXNode](./puppeteer.serializedaxnode.md) | Represents a Node and the properties of it that are relevant to Accessibility. |
|
||||
| [SnapshotOptions](./puppeteer.snapshotoptions.md) | |
|
||||
| [TracingOptions](./puppeteer.tracingoptions.md) | |
|
||||
| [Viewport](./puppeteer.viewport.md) | Sets the viewport of the page. |
|
||||
| [WaitForOptions](./puppeteer.waitforoptions.md) | |
|
||||
| [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | |
|
||||
| [WaitForTargetOptions](./puppeteer.waitfortargetoptions.md) | |
|
||||
| [WaitTimeoutOptions](./puppeteer.waittimeoutoptions.md) | |
|
||||
|
||||
## Variables
|
||||
|
||||
|
@ -4,8 +4,6 @@ sidebar_label: CustomQueryHandler
|
||||
|
||||
# CustomQueryHandler interface
|
||||
|
||||
Contains two functions `queryOne` and `queryAll` that can be [registered](./puppeteer.registercustomqueryhandler.md) as alternative querying strategies. The functions `queryOne` and `queryAll` are executed in the page context. `queryOne` should take an `Element` and a selector string as argument and return a single `Element` or `null` if no element is found. `queryAll` takes the same arguments but should instead return a `NodeListOf<Element>` or `Array<Element>` with all the elements that match the given query selector.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
@ -14,7 +12,7 @@ export interface CustomQueryHandler
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| ------------------------------------------------------- | --------- | ---------------------------------------------------- | ----------------- |
|
||||
| [queryAll?](./puppeteer.customqueryhandler.queryall.md) | | (element: Node, selector: string) => Node\[\] | <i>(Optional)</i> |
|
||||
| [queryOne?](./puppeteer.customqueryhandler.queryone.md) | | (element: Node, selector: string) => Node \| null | <i>(Optional)</i> |
|
||||
| Property | Modifiers | Type | Description |
|
||||
| ------------------------------------------------------- | --------- | ------------------------------------------------- | ----------------- |
|
||||
| [queryAll?](./puppeteer.customqueryhandler.queryall.md) | | (node: Node, selector: string) => Node\[\] | <i>(Optional)</i> |
|
||||
| [queryOne?](./puppeteer.customqueryhandler.queryone.md) | | (node: Node, selector: string) => Node \| null | <i>(Optional)</i> |
|
||||
|
@ -8,6 +8,6 @@ sidebar_label: CustomQueryHandler.queryAll
|
||||
|
||||
```typescript
|
||||
interface CustomQueryHandler {
|
||||
queryAll?: (element: Node, selector: string) => Node[];
|
||||
queryAll?: (node: Node, selector: string) => Node[];
|
||||
}
|
||||
```
|
||||
|
@ -8,6 +8,6 @@ sidebar_label: CustomQueryHandler.queryOne
|
||||
|
||||
```typescript
|
||||
interface CustomQueryHandler {
|
||||
queryOne?: (element: Node, selector: string) => Node | null;
|
||||
queryOne?: (node: Node, selector: string) => Node | null;
|
||||
}
|
||||
```
|
||||
|
@ -4,7 +4,11 @@ sidebar_label: ElementHandle.$x
|
||||
|
||||
# ElementHandle.$x() method
|
||||
|
||||
The method evaluates the XPath expression relative to the elementHandle. If there are no such elements, the method will resolve to an empty array.
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
> Use [ElementHandle.$$()](./puppeteer.elementhandle.__.md) with the `xpath` prefix.
|
||||
>
|
||||
> The method evaluates the XPath expression relative to the elementHandle. If there are no such elements, the method will resolve to an empty array.
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
@ -41,54 +41,32 @@ The constructor for this class is marked as internal. Third-party code should no
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Modifiers | Description |
|
||||
| -------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [$(selector)](./puppeteer.elementhandle._.md) | | Runs <code>element.querySelector</code> within the page. |
|
||||
| [$$(selector)](./puppeteer.elementhandle.__.md) | | Runs <code>element.querySelectorAll</code> within the page. |
|
||||
| [$$eval(selector, pageFunction, args)](./puppeteer.elementhandle.__eval.md) | | <p>This method runs <code>document.querySelectorAll</code> within the element and passes it as the first argument to <code>pageFunction</code>. If there's no element matching <code>selector</code>, the method throws an error.</p><p>If <code>pageFunction</code> returns a Promise, then <code>frame.$$eval</code> would wait for the promise to resolve and return its value.</p> |
|
||||
| [$eval(selector, pageFunction, args)](./puppeteer.elementhandle._eval.md) | | <p>This method runs <code>document.querySelector</code> within the element and passes it as the first argument to <code>pageFunction</code>. If there's no element matching <code>selector</code>, the method throws an error.</p><p>If <code>pageFunction</code> returns a Promise, then <code>frame.$eval</code> would wait for the promise to resolve and return its value.</p> |
|
||||
| [$x(expression)](./puppeteer.elementhandle._x.md) | | The method evaluates the XPath expression relative to the elementHandle. If there are no such elements, the method will resolve to an empty array. |
|
||||
| [asElement()](./puppeteer.elementhandle.aselement.md) | | |
|
||||
| [boundingBox()](./puppeteer.elementhandle.boundingbox.md) | | This method returns the bounding box of the element (relative to the main frame), or <code>null</code> if the element is not visible. |
|
||||
| [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or <code>null</code> if the element is not visible. |
|
||||
| [click(this, options)](./puppeteer.elementhandle.click.md) | | This method scrolls element into view if needed, and then uses [Page.mouse](./puppeteer.page.mouse.md) to click in the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [clickablePoint(offset)](./puppeteer.elementhandle.clickablepoint.md) | | Returns the middle point within an element unless a specific offset is provided. |
|
||||
| [contentFrame()](./puppeteer.elementhandle.contentframe.md) | | Resolves to the content frame for element handles referencing iframe nodes, or null otherwise |
|
||||
| [drag(this, target)](./puppeteer.elementhandle.drag.md) | | This method creates and captures a dragevent from the element. |
|
||||
| [dragAndDrop(this, target, options)](./puppeteer.elementhandle.draganddrop.md) | | This method triggers a dragenter, dragover, and drop on the element. |
|
||||
| [dragEnter(this, data)](./puppeteer.elementhandle.dragenter.md) | | This method creates a <code>dragenter</code> event on the element. |
|
||||
| [dragOver(this, data)](./puppeteer.elementhandle.dragover.md) | | This method creates a <code>dragover</code> event on the element. |
|
||||
| [drop(this, data)](./puppeteer.elementhandle.drop.md) | | This method triggers a drop on the element. |
|
||||
| [focus()](./puppeteer.elementhandle.focus.md) | | Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element. |
|
||||
| [hover(this)](./puppeteer.elementhandle.hover.md) | | This method scrolls element into view if needed, and then uses [Page.mouse](./puppeteer.page.mouse.md) to hover over the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [isIntersectingViewport(this, options)](./puppeteer.elementhandle.isintersectingviewport.md) | | Resolves to true if the element is visible in the current viewport. |
|
||||
| [press(key, options)](./puppeteer.elementhandle.press.md) | | Focuses the element, and then uses [Keyboard.down()](./puppeteer.keyboard.down.md) and [Keyboard.up()](./puppeteer.keyboard.up.md). |
|
||||
| [screenshot(this, options)](./puppeteer.elementhandle.screenshot.md) | | This method scrolls element into view if needed, and then uses [Page.screenshot()](./puppeteer.page.screenshot.md) to take a screenshot of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [select(values)](./puppeteer.elementhandle.select.md) | | Triggers a <code>change</code> and <code>input</code> event once all the provided options have been selected. If there's no <code><select></code> element matching <code>selector</code>, the method throws an error. |
|
||||
| [tap(this)](./puppeteer.elementhandle.tap.md) | | This method scrolls element into view if needed, and then uses [Touchscreen.tap()](./puppeteer.touchscreen.tap.md) to tap in the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [type(text, options)](./puppeteer.elementhandle.type.md) | | <p>Focuses the element, and then sends a <code>keydown</code>, <code>keypress</code>/<code>input</code>, and <code>keyup</code> event for each character in the text.</p><p>To press a special key, like <code>Control</code> or <code>ArrowDown</code>, use [ElementHandle.press()](./puppeteer.elementhandle.press.md).</p> |
|
||||
| [uploadFile(this, filePaths)](./puppeteer.elementhandle.uploadfile.md) | | This method expects <code>elementHandle</code> to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). |
|
||||
| [waitForSelector(selector, options)](./puppeteer.elementhandle.waitforselector.md) | | <p>Wait for the <code>selector</code> to appear within the element. If at the moment of calling the method the <code>selector</code> already exists, the method will return immediately. If the <code>selector</code> doesn't appear after the <code>timeout</code> milliseconds of waiting, the function will throw.</p><p>This method does not work across navigations or if the element is detached from DOM.</p> |
|
||||
| [waitForXPath(xpath, options)](./puppeteer.elementhandle.waitforxpath.md) | | <p>Wait for the <code>xpath</code> within the element. If at the moment of calling the method the <code>xpath</code> already exists, the method will return immediately. If the <code>xpath</code> doesn't appear after the <code>timeout</code> milliseconds of waiting, the function will throw.</p><p>If <code>xpath</code> starts with <code>//</code> instead of <code>.//</code>, the dot will be appended automatically.</p><p>This method works across navigation</p> |
|
||||
|
||||
```ts
|
||||
const puppeteer = require('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();
|
||||
})();
|
||||
```
|
||||
|
||||
|
|
||||
| Method | Modifiers | Description |
|
||||
| -------------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [$(selector)](./puppeteer.elementhandle._.md) | | Runs <code>element.querySelector</code> within the page. |
|
||||
| [$$(selector)](./puppeteer.elementhandle.__.md) | | Runs <code>element.querySelectorAll</code> within the page. |
|
||||
| [$$eval(selector, pageFunction, args)](./puppeteer.elementhandle.__eval.md) | | <p>This method runs <code>document.querySelectorAll</code> within the element and passes it as the first argument to <code>pageFunction</code>. If there's no element matching <code>selector</code>, the method throws an error.</p><p>If <code>pageFunction</code> returns a Promise, then <code>frame.$$eval</code> would wait for the promise to resolve and return its value.</p> |
|
||||
| [$eval(selector, pageFunction, args)](./puppeteer.elementhandle._eval.md) | | <p>This method runs <code>document.querySelector</code> within the element and passes it as the first argument to <code>pageFunction</code>. If there's no element matching <code>selector</code>, the method throws an error.</p><p>If <code>pageFunction</code> returns a Promise, then <code>frame.$eval</code> would wait for the promise to resolve and return its value.</p> |
|
||||
| [$x(expression)](./puppeteer.elementhandle._x.md) | | |
|
||||
| [asElement()](./puppeteer.elementhandle.aselement.md) | | |
|
||||
| [boundingBox()](./puppeteer.elementhandle.boundingbox.md) | | This method returns the bounding box of the element (relative to the main frame), or <code>null</code> if the element is not visible. |
|
||||
| [boxModel()](./puppeteer.elementhandle.boxmodel.md) | | This method returns boxes of the element, or <code>null</code> if the element is not visible. |
|
||||
| [click(this, options)](./puppeteer.elementhandle.click.md) | | This method scrolls element into view if needed, and then uses [Page.mouse](./puppeteer.page.mouse.md) to click in the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [clickablePoint(offset)](./puppeteer.elementhandle.clickablepoint.md) | | Returns the middle point within an element unless a specific offset is provided. |
|
||||
| [contentFrame()](./puppeteer.elementhandle.contentframe.md) | | Resolves to the content frame for element handles referencing iframe nodes, or null otherwise |
|
||||
| [drag(this, target)](./puppeteer.elementhandle.drag.md) | | This method creates and captures a dragevent from the element. |
|
||||
| [dragAndDrop(this, target, options)](./puppeteer.elementhandle.draganddrop.md) | | This method triggers a dragenter, dragover, and drop on the element. |
|
||||
| [dragEnter(this, data)](./puppeteer.elementhandle.dragenter.md) | | This method creates a <code>dragenter</code> event on the element. |
|
||||
| [dragOver(this, data)](./puppeteer.elementhandle.dragover.md) | | This method creates a <code>dragover</code> event on the element. |
|
||||
| [drop(this, data)](./puppeteer.elementhandle.drop.md) | | This method triggers a drop on the element. |
|
||||
| [focus()](./puppeteer.elementhandle.focus.md) | | Calls [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) on the element. |
|
||||
| [hover(this)](./puppeteer.elementhandle.hover.md) | | This method scrolls element into view if needed, and then uses [Page.mouse](./puppeteer.page.mouse.md) to hover over the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [isIntersectingViewport(this, options)](./puppeteer.elementhandle.isintersectingviewport.md) | | Resolves to true if the element is visible in the current viewport. |
|
||||
| [press(key, options)](./puppeteer.elementhandle.press.md) | | Focuses the element, and then uses [Keyboard.down()](./puppeteer.keyboard.down.md) and [Keyboard.up()](./puppeteer.keyboard.up.md). |
|
||||
| [screenshot(this, options)](./puppeteer.elementhandle.screenshot.md) | | This method scrolls element into view if needed, and then uses [Page.screenshot()](./puppeteer.page.screenshot.md) to take a screenshot of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [select(values)](./puppeteer.elementhandle.select.md) | | Triggers a <code>change</code> and <code>input</code> event once all the provided options have been selected. If there's no <code><select></code> element matching <code>selector</code>, the method throws an error. |
|
||||
| [tap(this)](./puppeteer.elementhandle.tap.md) | | This method scrolls element into view if needed, and then uses [Touchscreen.tap()](./puppeteer.touchscreen.tap.md) to tap in the center of the element. If the element is detached from DOM, the method throws an error. |
|
||||
| [type(text, options)](./puppeteer.elementhandle.type.md) | | <p>Focuses the element, and then sends a <code>keydown</code>, <code>keypress</code>/<code>input</code>, and <code>keyup</code> event for each character in the text.</p><p>To press a special key, like <code>Control</code> or <code>ArrowDown</code>, use [ElementHandle.press()](./puppeteer.elementhandle.press.md).</p> |
|
||||
| [uploadFile(this, filePaths)](./puppeteer.elementhandle.uploadfile.md) | | This method expects <code>elementHandle</code> to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input). |
|
||||
| [waitForSelector(selector, options)](./puppeteer.elementhandle.waitforselector.md) | | <p>Wait for the <code>selector</code> to appear within the element. If at the moment of calling the method the <code>selector</code> already exists, the method will return immediately. If the <code>selector</code> doesn't appear after the <code>timeout</code> milliseconds of waiting, the function will throw.</p><p>This method does not work across navigations or if the element is detached from DOM.</p> |
|
||||
| [waitForXPath(xpath, options)](./puppeteer.elementhandle.waitforxpath.md) | | |
|
||||
|
@ -4,31 +4,35 @@ sidebar_label: ElementHandle.waitForXPath
|
||||
|
||||
# ElementHandle.waitForXPath() method
|
||||
|
||||
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.
|
||||
|
||||
This method works across navigation
|
||||
|
||||
```ts
|
||||
const puppeteer = require('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();
|
||||
})();
|
||||
```
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
> Use [ElementHandle.waitForSelector()](./puppeteer.elementhandle.waitforselector.md) with the `xpath` prefix.
|
||||
>
|
||||
> 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.
|
||||
>
|
||||
> This method works across navigation
|
||||
>
|
||||
> ```ts
|
||||
> const puppeteer = require('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();
|
||||
> })();
|
||||
> ```
|
||||
|
||||
**Signature:**
|
||||
|
||||
|
@ -4,6 +4,14 @@ sidebar_label: Frame.waitForXPath
|
||||
|
||||
# Frame.waitForXPath() method
|
||||
|
||||
> Warning: This API is now obsolete.
|
||||
>
|
||||
> Use [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md) with the `xpath` prefix.
|
||||
>
|
||||
> 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
|
||||
@ -25,9 +33,3 @@ class Frame {
|
||||
**Returns:**
|
||||
|
||||
Promise<[ElementHandle](./puppeteer.elementhandle.md)<Node> \| null>
|
||||
|
||||
## Remarks
|
||||
|
||||
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.
|
||||
|
@ -114,7 +114,7 @@ const waitFor = async (
|
||||
return (await domWorld._waitForSelectorInPage(
|
||||
(_: Element, selector: string) => {
|
||||
return (
|
||||
globalThis as any as unknown as {
|
||||
globalThis as unknown as {
|
||||
ariaQuerySelector(selector: string): void;
|
||||
}
|
||||
).ariaQuerySelector(selector);
|
||||
|
@ -717,52 +717,6 @@ export class DOMWorld {
|
||||
return elementHandle;
|
||||
}
|
||||
|
||||
async waitForXPath(
|
||||
xpath: string,
|
||||
options: WaitForSelectorOptions
|
||||
): Promise<ElementHandle<Node> | null> {
|
||||
const {
|
||||
visible: waitForVisible = false,
|
||||
hidden: waitForHidden = false,
|
||||
timeout = this.#timeoutSettings.timeout(),
|
||||
} = options;
|
||||
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
|
||||
const title = `XPath \`${xpath}\`${waitForHidden ? ' to be hidden' : ''}`;
|
||||
function predicate(
|
||||
root: Element | Document,
|
||||
xpath: string,
|
||||
waitForVisible: boolean,
|
||||
waitForHidden: boolean
|
||||
): Node | null | boolean {
|
||||
const node = document.evaluate(
|
||||
xpath,
|
||||
root,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE,
|
||||
null
|
||||
).singleNodeValue;
|
||||
return checkWaitForOptions(node, waitForVisible, waitForHidden);
|
||||
}
|
||||
const waitTaskOptions: WaitTaskOptions = {
|
||||
domWorld: this,
|
||||
predicateBody: makePredicateString(predicate),
|
||||
predicateAcceptsContextElement: true,
|
||||
title,
|
||||
polling,
|
||||
timeout,
|
||||
args: [xpath, waitForVisible, waitForHidden],
|
||||
root: options.root,
|
||||
};
|
||||
const waitTask = new WaitTask(waitTaskOptions);
|
||||
const jsHandle = await waitTask.promise;
|
||||
const elementHandle = jsHandle.asElement();
|
||||
if (!elementHandle) {
|
||||
await jsHandle.dispose();
|
||||
return null;
|
||||
}
|
||||
return elementHandle;
|
||||
}
|
||||
|
||||
waitForFunction(
|
||||
pageFunction: Function | string,
|
||||
options: {polling?: string | number; timeout?: number} = {},
|
||||
|
@ -140,6 +140,8 @@ export class ElementHandle<
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ElementHandle.waitForSelector} with the `xpath` prefix.
|
||||
*
|
||||
* 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
|
||||
@ -197,27 +199,10 @@ export class ElementHandle<
|
||||
timeout?: number;
|
||||
} = {}
|
||||
): Promise<ElementHandle<Node> | null> {
|
||||
const frame = this._context.frame();
|
||||
assert(frame);
|
||||
const secondaryContext = await frame._secondaryWorld.executionContext();
|
||||
const adoptedRoot = await secondaryContext._adoptElementHandle(this);
|
||||
xpath = xpath.startsWith('//') ? '.' + xpath : xpath;
|
||||
if (!xpath.startsWith('.//')) {
|
||||
await adoptedRoot.dispose();
|
||||
throw new Error('Unsupported xpath expression: ' + xpath);
|
||||
if (xpath.startsWith('//')) {
|
||||
xpath = `.${xpath}`;
|
||||
}
|
||||
const handle = await frame._secondaryWorld.waitForXPath(xpath, {
|
||||
...options,
|
||||
root: adoptedRoot,
|
||||
});
|
||||
await adoptedRoot.dispose();
|
||||
if (!handle) {
|
||||
return null;
|
||||
}
|
||||
const mainExecutionContext = await frame._mainWorld.executionContext();
|
||||
const result = await mainExecutionContext._adoptElementHandle(handle);
|
||||
await handle.dispose();
|
||||
return result;
|
||||
return this.waitForSelector(`xpath/${xpath}`, options);
|
||||
}
|
||||
|
||||
override asElement(): ElementHandle<ElementType> | null {
|
||||
@ -964,36 +949,17 @@ export class ElementHandle<
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link ElementHandle.$$} with the `xpath` prefix.
|
||||
*
|
||||
* The method evaluates the XPath expression relative to the elementHandle.
|
||||
* 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}
|
||||
*/
|
||||
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
|
||||
const arrayHandle = await this.evaluateHandle((element, expression) => {
|
||||
const doc = element.ownerDocument || document;
|
||||
const iterator = doc.evaluate(
|
||||
expression,
|
||||
element,
|
||||
null,
|
||||
XPathResult.ORDERED_NODE_ITERATOR_TYPE
|
||||
);
|
||||
const array = [];
|
||||
let item;
|
||||
while ((item = iterator.iterateNext())) {
|
||||
array.push(item);
|
||||
}
|
||||
return array;
|
||||
}, expression);
|
||||
const properties = await arrayHandle.getProperties();
|
||||
await arrayHandle.dispose();
|
||||
const result = [];
|
||||
for (const property of properties.values()) {
|
||||
const elementHandle = property.asElement();
|
||||
if (elementHandle) {
|
||||
result.push(elementHandle);
|
||||
}
|
||||
if (expression.startsWith('//')) {
|
||||
expression = `.${expression}`;
|
||||
}
|
||||
return result;
|
||||
return this.$$(`xpath/${expression}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1374,7 +1374,8 @@ export class Frame {
|
||||
}
|
||||
|
||||
/**
|
||||
* @remarks
|
||||
* @deprecated Use {@link Frame.waitForSelector} with the `xpath` prefix.
|
||||
*
|
||||
* 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
|
||||
@ -1392,14 +1393,10 @@ export class Frame {
|
||||
xpath: string,
|
||||
options: WaitForSelectorOptions = {}
|
||||
): Promise<ElementHandle<Node> | null> {
|
||||
const handle = await this._secondaryWorld.waitForXPath(xpath, options);
|
||||
if (!handle) {
|
||||
return null;
|
||||
if (xpath.startsWith('//')) {
|
||||
xpath = `.${xpath}`;
|
||||
}
|
||||
const mainExecutionContext = await this._mainWorld.executionContext();
|
||||
const result = await mainExecutionContext._adoptElementHandle(handle);
|
||||
await handle.dispose();
|
||||
return result;
|
||||
return this.waitForSelector(`xpath/${xpath}`, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,47 +19,66 @@ import {DOMWorld, WaitForSelectorOptions} from './DOMWorld.js';
|
||||
import {ElementHandle} from './ElementHandle.js';
|
||||
import {JSHandle} from './JSHandle.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface CustomQueryHandler {
|
||||
/**
|
||||
* @returns A {@link Node} matching the given {@link selector} from {@link node}.
|
||||
*/
|
||||
queryOne?: (node: Node, selector: string) => Node | null;
|
||||
/**
|
||||
* @returns Some {@link Node}s matching the given {@link selector} from {@link node}.
|
||||
*/
|
||||
queryAll?: (node: Node, selector: string) => Node[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface InternalQueryHandler {
|
||||
/**
|
||||
* Queries for a single node given a selector and {@link ElementHandle}.
|
||||
*
|
||||
* Akin to {@link Window.prototype.querySelector}.
|
||||
*/
|
||||
queryOne?: (
|
||||
element: ElementHandle<Node>,
|
||||
selector: string
|
||||
) => Promise<ElementHandle<Node> | null>;
|
||||
/**
|
||||
* Queries for multiple nodes given a selector and {@link ElementHandle}.
|
||||
*
|
||||
* Akin to {@link Window.prototype.querySelectorAll}.
|
||||
*/
|
||||
queryAll?: (
|
||||
element: ElementHandle<Node>,
|
||||
selector: string
|
||||
) => Promise<Array<ElementHandle<Node>>>;
|
||||
|
||||
/**
|
||||
* Queries for multiple nodes given a selector and {@link ElementHandle}.
|
||||
* Unlike {@link queryAll}, this returns a handle to a node array.
|
||||
*
|
||||
* Akin to {@link Window.prototype.querySelectorAll}.
|
||||
*/
|
||||
queryAllArray?: (
|
||||
element: ElementHandle<Node>,
|
||||
selector: string
|
||||
) => Promise<JSHandle<Node[]>>;
|
||||
/**
|
||||
* Waits until a single node appears for a given selector and
|
||||
* {@link ElementHandle}.
|
||||
*
|
||||
* Akin to {@link Window.prototype.querySelectorAll}.
|
||||
*/
|
||||
waitFor?: (
|
||||
domWorld: DOMWorld,
|
||||
selector: string,
|
||||
options: WaitForSelectorOptions
|
||||
) => Promise<ElementHandle<Node> | null>;
|
||||
queryAllArray?: (
|
||||
element: ElementHandle<Node>,
|
||||
selector: string
|
||||
) => Promise<JSHandle<Node[]>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains two functions `queryOne` and `queryAll` that can
|
||||
* be {@link registerCustomQueryHandler | registered}
|
||||
* as alternative querying strategies. The functions `queryOne` and `queryAll`
|
||||
* are executed in the page context. `queryOne` should take an `Element` and a
|
||||
* selector string as argument and return a single `Element` or `null` if no
|
||||
* element is found. `queryAll` takes the same arguments but should instead
|
||||
* return a `NodeListOf<Element>` or `Array<Element>` with all the elements
|
||||
* that match the given query selector.
|
||||
* @public
|
||||
*/
|
||||
export interface CustomQueryHandler {
|
||||
queryOne?: (element: Node, selector: string) => Node | null;
|
||||
queryAll?: (element: Node, selector: string) => Node[];
|
||||
}
|
||||
|
||||
function createInternalQueryHandler(
|
||||
function internalizeCustomQueryHandler(
|
||||
handler: CustomQueryHandler
|
||||
): InternalQueryHandler {
|
||||
const internalHandler: InternalQueryHandler = {};
|
||||
@ -114,7 +133,7 @@ function createInternalQueryHandler(
|
||||
return internalHandler;
|
||||
}
|
||||
|
||||
const defaultHandler = createInternalQueryHandler({
|
||||
const defaultHandler = internalizeCustomQueryHandler({
|
||||
queryOne: (element, selector) => {
|
||||
if (!('querySelector' in element)) {
|
||||
throw new Error(
|
||||
@ -141,7 +160,7 @@ const defaultHandler = createInternalQueryHandler({
|
||||
},
|
||||
});
|
||||
|
||||
const pierceHandler = createInternalQueryHandler({
|
||||
const pierceHandler = internalizeCustomQueryHandler({
|
||||
queryOne: (element, selector) => {
|
||||
let found: Node | null = null;
|
||||
const search = (root: Node) => {
|
||||
@ -191,11 +210,46 @@ const pierceHandler = createInternalQueryHandler({
|
||||
},
|
||||
});
|
||||
|
||||
const builtInHandlers = new Map([
|
||||
['aria', ariaHandler],
|
||||
['pierce', pierceHandler],
|
||||
const xpathHandler = internalizeCustomQueryHandler({
|
||||
queryOne: (element, selector) => {
|
||||
const doc = element.ownerDocument || document;
|
||||
const result = doc.evaluate(
|
||||
selector,
|
||||
element,
|
||||
null,
|
||||
XPathResult.FIRST_ORDERED_NODE_TYPE
|
||||
);
|
||||
return result.singleNodeValue;
|
||||
},
|
||||
|
||||
queryAll: (element, selector) => {
|
||||
const doc = element.ownerDocument || document;
|
||||
const iterator = doc.evaluate(
|
||||
selector,
|
||||
element,
|
||||
null,
|
||||
XPathResult.ORDERED_NODE_ITERATOR_TYPE
|
||||
);
|
||||
const array: Node[] = [];
|
||||
let item;
|
||||
while ((item = iterator.iterateNext())) {
|
||||
array.push(item);
|
||||
}
|
||||
return array;
|
||||
},
|
||||
});
|
||||
|
||||
interface RegisteredQueryHandler {
|
||||
handler: InternalQueryHandler;
|
||||
transformSelector?: (selector: string) => string;
|
||||
}
|
||||
|
||||
const INTERNAL_QUERY_HANDLERS = new Map<string, RegisteredQueryHandler>([
|
||||
['aria', {handler: ariaHandler}],
|
||||
['pierce', {handler: pierceHandler}],
|
||||
['xpath', {handler: xpathHandler}],
|
||||
]);
|
||||
const queryHandlers = new Map(builtInHandlers);
|
||||
const QUERY_HANDLERS = new Map<string, RegisteredQueryHandler>();
|
||||
|
||||
/**
|
||||
* Registers a {@link CustomQueryHandler | custom query handler}.
|
||||
@ -222,7 +276,10 @@ export function registerCustomQueryHandler(
|
||||
name: string,
|
||||
handler: CustomQueryHandler
|
||||
): void {
|
||||
if (queryHandlers.get(name)) {
|
||||
if (INTERNAL_QUERY_HANDLERS.has(name)) {
|
||||
throw new Error(`A query handler named "${name}" already exists`);
|
||||
}
|
||||
if (QUERY_HANDLERS.has(name)) {
|
||||
throw new Error(`A custom query handler named "${name}" already exists`);
|
||||
}
|
||||
|
||||
@ -231,9 +288,7 @@ export function registerCustomQueryHandler(
|
||||
throw new Error(`Custom query handler names may only contain [a-zA-Z]`);
|
||||
}
|
||||
|
||||
const internalHandler = createInternalQueryHandler(handler);
|
||||
|
||||
queryHandlers.set(name, internalHandler);
|
||||
QUERY_HANDLERS.set(name, {handler: internalizeCustomQueryHandler(handler)});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -242,9 +297,7 @@ export function registerCustomQueryHandler(
|
||||
* @public
|
||||
*/
|
||||
export function unregisterCustomQueryHandler(name: string): void {
|
||||
if (queryHandlers.has(name) && !builtInHandlers.has(name)) {
|
||||
queryHandlers.delete(name);
|
||||
}
|
||||
QUERY_HANDLERS.delete(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,9 +306,7 @@ export function unregisterCustomQueryHandler(name: string): void {
|
||||
* @public
|
||||
*/
|
||||
export function customQueryHandlerNames(): string[] {
|
||||
return [...queryHandlers.keys()].filter(name => {
|
||||
return !builtInHandlers.has(name);
|
||||
});
|
||||
return [...QUERY_HANDLERS.keys()];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -264,9 +315,11 @@ export function customQueryHandlerNames(): string[] {
|
||||
* @public
|
||||
*/
|
||||
export function clearCustomQueryHandlers(): void {
|
||||
customQueryHandlerNames().forEach(unregisterCustomQueryHandler);
|
||||
QUERY_HANDLERS.clear();
|
||||
}
|
||||
|
||||
const CUSTOM_QUERY_SEPARATORS = ['=', '/'];
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -274,23 +327,22 @@ export function getQueryHandlerAndSelector(selector: string): {
|
||||
updatedSelector: string;
|
||||
queryHandler: InternalQueryHandler;
|
||||
} {
|
||||
const hasCustomQueryHandler = /^[a-zA-Z]+\//.test(selector);
|
||||
if (!hasCustomQueryHandler) {
|
||||
return {updatedSelector: selector, queryHandler: defaultHandler};
|
||||
for (const handlerMap of [QUERY_HANDLERS, INTERNAL_QUERY_HANDLERS]) {
|
||||
for (const [
|
||||
name,
|
||||
{handler: queryHandler, transformSelector},
|
||||
] of handlerMap) {
|
||||
for (const separator of CUSTOM_QUERY_SEPARATORS) {
|
||||
const prefix = `${name}${separator}`;
|
||||
if (selector.startsWith(prefix)) {
|
||||
selector = selector.slice(prefix.length);
|
||||
if (transformSelector) {
|
||||
selector = transformSelector(selector);
|
||||
}
|
||||
return {updatedSelector: selector, queryHandler};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const index = selector.indexOf('/');
|
||||
const name = selector.slice(0, index);
|
||||
const updatedSelector = selector.slice(index + 1);
|
||||
const queryHandler = queryHandlers.get(name);
|
||||
if (!queryHandler) {
|
||||
throw new Error(
|
||||
`Query set to use "${name}", but no query handler of that name was found`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
updatedSelector,
|
||||
queryHandler,
|
||||
};
|
||||
return {updatedSelector: selector, queryHandler: defaultHandler};
|
||||
}
|
||||
|
@ -319,12 +319,12 @@ describe('ElementHandle specs', function () {
|
||||
</div>`
|
||||
);
|
||||
|
||||
const el2 = (await page.waitForSelector(
|
||||
const el1 = (await page.waitForSelector(
|
||||
'#el1'
|
||||
)) as ElementHandle<HTMLDivElement>;
|
||||
|
||||
for (const path of ['//div', './/div']) {
|
||||
const e = (await el2.waitForXPath(
|
||||
const e = (await el1.waitForXPath(
|
||||
path
|
||||
)) as ElementHandle<HTMLDivElement>;
|
||||
expect(
|
||||
@ -423,10 +423,8 @@ describe('ElementHandle specs', function () {
|
||||
await page.$('getById/foo');
|
||||
throw new Error('Custom query handler name not set - throw expected');
|
||||
} catch (error) {
|
||||
expect(error).toStrictEqual(
|
||||
new Error(
|
||||
'Query set to use "getById", but no query handler of that name was found'
|
||||
)
|
||||
expect(error).not.toStrictEqual(
|
||||
new Error('Custom query handler name not set - throw expected')
|
||||
);
|
||||
}
|
||||
const handlerNamesAfterUnregistering =
|
||||
|
160
test/src/queryhandler.spec.ts
Normal file
160
test/src/queryhandler.spec.ts
Normal file
@ -0,0 +1,160 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
import expect from 'expect';
|
||||
import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js';
|
||||
import {
|
||||
getTestState,
|
||||
setupTestBrowserHooks,
|
||||
setupTestPageAndContextHooks,
|
||||
} from './mocha-utils.js';
|
||||
|
||||
describe('Query handler tests', function () {
|
||||
setupTestBrowserHooks();
|
||||
setupTestPageAndContextHooks();
|
||||
|
||||
describe('Pierce selectors', function () {
|
||||
beforeEach(async () => {
|
||||
const {page} = getTestState();
|
||||
await page.setContent(
|
||||
`<script>
|
||||
const div = document.createElement('div');
|
||||
const shadowRoot = div.attachShadow({mode: 'open'});
|
||||
const div1 = document.createElement('div');
|
||||
div1.textContent = 'Hello';
|
||||
div1.className = 'foo';
|
||||
const div2 = document.createElement('div');
|
||||
div2.textContent = 'World';
|
||||
div2.className = 'foo';
|
||||
shadowRoot.appendChild(div1);
|
||||
shadowRoot.appendChild(div2);
|
||||
document.documentElement.appendChild(div);
|
||||
</script>`
|
||||
);
|
||||
});
|
||||
it('should find first element in shadow', async () => {
|
||||
const {page} = getTestState();
|
||||
const div = (await page.$('pierce/.foo')) as ElementHandle<HTMLElement>;
|
||||
const text = await div.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
expect(text).toBe('Hello');
|
||||
});
|
||||
it('should find all elements in shadow', async () => {
|
||||
const {page} = getTestState();
|
||||
const divs = (await page.$$('pierce/.foo')) as Array<
|
||||
ElementHandle<HTMLElement>
|
||||
>;
|
||||
const text = await Promise.all(
|
||||
divs.map(div => {
|
||||
return div.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
})
|
||||
);
|
||||
expect(text.join(' ')).toBe('Hello World');
|
||||
});
|
||||
it('should find first child element', async () => {
|
||||
const {page} = getTestState();
|
||||
const parentElement = (await page.$('html > div'))!;
|
||||
const childElement = (await parentElement.$(
|
||||
'pierce/div'
|
||||
)) as ElementHandle<HTMLElement>;
|
||||
const text = await childElement.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
expect(text).toBe('Hello');
|
||||
});
|
||||
it('should find all child elements', async () => {
|
||||
const {page} = getTestState();
|
||||
const parentElement = (await page.$('html > div'))!;
|
||||
const childElements = (await parentElement.$$('pierce/div')) as Array<
|
||||
ElementHandle<HTMLElement>
|
||||
>;
|
||||
const text = await Promise.all(
|
||||
childElements.map(div => {
|
||||
return div.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
})
|
||||
);
|
||||
expect(text.join(' ')).toBe('Hello World');
|
||||
});
|
||||
});
|
||||
|
||||
describe('XPath selectors', function () {
|
||||
describe('in Page', function () {
|
||||
it('should query existing element', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
await page.setContent('<section>test</section>');
|
||||
|
||||
expect(await page.$('xpath/html/body/section')).toBeTruthy();
|
||||
expect((await page.$$('xpath/html/body/section')).length).toBe(1);
|
||||
});
|
||||
it('should return empty array for non-existing element', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
expect(
|
||||
await page.$('xpath/html/body/non-existing-element')
|
||||
).toBeFalsy();
|
||||
expect(
|
||||
(await page.$$('xpath/html/body/non-existing-element')).length
|
||||
).toBe(0);
|
||||
});
|
||||
it('should return first element', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
await page.setContent('<div>a</div><div></div>');
|
||||
|
||||
const element = await page.$('xpath/html/body/div');
|
||||
expect(
|
||||
await element?.evaluate(e => {
|
||||
return e.textContent === 'a';
|
||||
})
|
||||
).toBeTruthy();
|
||||
});
|
||||
it('should return multiple elements', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
await page.setContent('<div></div><div></div>');
|
||||
|
||||
const elements = await page.$$('xpath/html/body/div');
|
||||
expect(elements.length).toBe(2);
|
||||
});
|
||||
});
|
||||
describe('in ElementHandles', function () {
|
||||
it('should query existing element', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
await page.setContent('<div class="a">a<span></span></div>');
|
||||
|
||||
const elementHandle = (await page.$('div'))!;
|
||||
expect(await elementHandle.$(`xpath/span`)).toBeTruthy();
|
||||
expect((await elementHandle.$$(`xpath/span`)).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should return null for non-existing element', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
await page.setContent('<div class="a">a</div>');
|
||||
|
||||
const elementHandle = (await page.$('div'))!;
|
||||
expect(await elementHandle.$(`xpath/span`)).toBeFalsy();
|
||||
expect((await elementHandle.$$(`xpath/span`)).length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -14,13 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import expect from 'expect';
|
||||
import {CustomQueryHandler} from '../../lib/cjs/puppeteer/common/QueryHandler.js';
|
||||
import {
|
||||
getTestState,
|
||||
setupTestBrowserHooks,
|
||||
setupTestPageAndContextHooks,
|
||||
} from './mocha-utils.js';
|
||||
import {CustomQueryHandler} from '../../lib/cjs/puppeteer/common/QueryHandler.js';
|
||||
import {ElementHandle} from '../../lib/cjs/puppeteer/common/ElementHandle.js';
|
||||
|
||||
describe('querySelector', function () {
|
||||
setupTestBrowserHooks();
|
||||
@ -79,75 +78,6 @@ describe('querySelector', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('pierceHandler', function () {
|
||||
beforeEach(async () => {
|
||||
const {page} = getTestState();
|
||||
await page.setContent(
|
||||
`<script>
|
||||
const div = document.createElement('div');
|
||||
const shadowRoot = div.attachShadow({mode: 'open'});
|
||||
const div1 = document.createElement('div');
|
||||
div1.textContent = 'Hello';
|
||||
div1.className = 'foo';
|
||||
const div2 = document.createElement('div');
|
||||
div2.textContent = 'World';
|
||||
div2.className = 'foo';
|
||||
shadowRoot.appendChild(div1);
|
||||
shadowRoot.appendChild(div2);
|
||||
document.documentElement.appendChild(div);
|
||||
</script>`
|
||||
);
|
||||
});
|
||||
it('should find first element in shadow', async () => {
|
||||
const {page} = getTestState();
|
||||
const div = (await page.$('pierce/.foo')) as ElementHandle<HTMLElement>;
|
||||
const text = await div.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
expect(text).toBe('Hello');
|
||||
});
|
||||
it('should find all elements in shadow', async () => {
|
||||
const {page} = getTestState();
|
||||
const divs = (await page.$$('pierce/.foo')) as Array<
|
||||
ElementHandle<HTMLElement>
|
||||
>;
|
||||
const text = await Promise.all(
|
||||
divs.map(div => {
|
||||
return div.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
})
|
||||
);
|
||||
expect(text.join(' ')).toBe('Hello World');
|
||||
});
|
||||
it('should find first child element', async () => {
|
||||
const {page} = getTestState();
|
||||
const parentElement = (await page.$('html > div'))!;
|
||||
const childElement = (await parentElement.$(
|
||||
'pierce/div'
|
||||
)) as ElementHandle<HTMLElement>;
|
||||
const text = await childElement.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
expect(text).toBe('Hello');
|
||||
});
|
||||
it('should find all child elements', async () => {
|
||||
const {page} = getTestState();
|
||||
const parentElement = (await page.$('html > div'))!;
|
||||
const childElements = (await parentElement.$$('pierce/div')) as Array<
|
||||
ElementHandle<HTMLElement>
|
||||
>;
|
||||
const text = await Promise.all(
|
||||
childElements.map(div => {
|
||||
return div.evaluate(element => {
|
||||
return element.textContent;
|
||||
});
|
||||
})
|
||||
);
|
||||
expect(text.join(' ')).toBe('Hello World');
|
||||
});
|
||||
});
|
||||
|
||||
// The tests for $$eval are repeated later in this file in the test group 'QueryAll'.
|
||||
// This is done to also test a query handler where QueryAll returns an Element[]
|
||||
// as opposed to NodeListOf<Element>.
|
||||
@ -256,7 +186,7 @@ describe('querySelector', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Path.$x', function () {
|
||||
describe('Page.$x', function () {
|
||||
it('should query existing element', async () => {
|
||||
const {page} = getTestState();
|
||||
|
||||
|
@ -735,7 +735,7 @@ describe('waittask specs', function () {
|
||||
});
|
||||
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
|
||||
expect(error?.message).toContain(
|
||||
'waiting for XPath `//div` failed: timeout'
|
||||
'waiting for selector `.//div` failed: timeout 10ms exceeded'
|
||||
);
|
||||
});
|
||||
itFailsFirefox('should run in specified frame', async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user