mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
feat: support AbortController in waitForSelector (#10018)
This commit is contained in:
parent
fd08e6ad22
commit
9109b76276
@ -8,6 +8,7 @@ sidebar_label: API
|
|||||||
|
|
||||||
| Class | Description |
|
| Class | Description |
|
||||||
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| [AbortError](./puppeteer.aborterror.md) | AbortError is emitted whenever certain operations are terminated due to an abort request. |
|
||||||
| [Accessibility](./puppeteer.accessibility.md) | The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access). |
|
| [Accessibility](./puppeteer.accessibility.md) | The Accessibility class provides methods for inspecting Chromium's accessibility tree. The accessibility tree is used by assistive technology such as [screen readers](https://en.wikipedia.org/wiki/Screen_reader) or [switches](https://en.wikipedia.org/wiki/Switch_access). |
|
||||||
| [Browser](./puppeteer.browser.md) | A Browser is created when Puppeteer connects to a Chromium instance, either through [PuppeteerNode.launch()](./puppeteer.puppeteernode.launch.md) or [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
|
| [Browser](./puppeteer.browser.md) | A Browser is created when Puppeteer connects to a Chromium instance, either through [PuppeteerNode.launch()](./puppeteer.puppeteernode.launch.md) or [Puppeteer.connect()](./puppeteer.puppeteer.connect.md). |
|
||||||
| [BrowserContext](./puppeteer.browsercontext.md) | BrowserContexts provide a way to operate multiple independent browser sessions. When a browser is launched, it has a single BrowserContext used by default. The method [Browser.newPage](./puppeteer.browser.newpage.md) creates a page in the default browser context. |
|
| [BrowserContext](./puppeteer.browsercontext.md) | BrowserContexts provide a way to operate multiple independent browser sessions. When a browser is launched, it has a single BrowserContext used by default. The method [Browser.newPage](./puppeteer.browser.newpage.md) creates a page in the default browser context. |
|
||||||
|
19
docs/api/puppeteer.aborterror.md
Normal file
19
docs/api/puppeteer.aborterror.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: AbortError
|
||||||
|
---
|
||||||
|
|
||||||
|
# AbortError class
|
||||||
|
|
||||||
|
AbortError is emitted whenever certain operations are terminated due to an abort request.
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export declare class AbortError extends CustomError
|
||||||
|
```
|
||||||
|
|
||||||
|
**Extends:** [CustomError](./puppeteer.customerror.md)
|
||||||
|
|
||||||
|
## Remarks
|
||||||
|
|
||||||
|
Example operations are [page.waitForSelector](./puppeteer.page.waitforselector.md).
|
@ -13,7 +13,8 @@ export interface WaitForSelectorOptions
|
|||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Modifiers | Type | Description | Default |
|
| Property | Modifiers | Type | Description | Default |
|
||||||
| -------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
|
| --------------- | --------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
|
||||||
|
| abortController | <code>optional</code> | AbortController | Provide an abort controller to cancel a waitForSelector call. | |
|
||||||
| hidden | <code>optional</code> | boolean | Wait for the selected element to not be found in the DOM or to be hidden, i.e. have <code>display: none</code> or <code>visibility: hidden</code> CSS properties. | <code>false</code> |
|
| hidden | <code>optional</code> | boolean | Wait for the selected element to not be found in the DOM or to be hidden, i.e. have <code>display: none</code> or <code>visibility: hidden</code> CSS properties. | <code>false</code> |
|
||||||
| timeout | <code>optional</code> | number | <p>Maximum time to wait in milliseconds. Pass <code>0</code> to disable timeout.</p><p>The default value can be changed by using [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md)</p> | <code>30_000</code> (30 seconds) |
|
| timeout | <code>optional</code> | number | <p>Maximum time to wait in milliseconds. Pass <code>0</code> to disable timeout.</p><p>The default value can be changed by using [Page.setDefaultTimeout()](./puppeteer.page.setdefaulttimeout.md)</p> | <code>30_000</code> (30 seconds) |
|
||||||
| visible | <code>optional</code> | boolean | Wait for the selected element to be present in DOM and to be visible, i.e. to not have <code>display: none</code> or <code>visibility: hidden</code> CSS properties. | <code>false</code> |
|
| visible | <code>optional</code> | boolean | Wait for the selected element to be present in DOM and to be visible, i.e. to not have <code>display: none</code> or <code>visibility: hidden</code> CSS properties. | <code>false</code> |
|
||||||
|
@ -42,6 +42,17 @@ export class CustomError extends Error {
|
|||||||
*/
|
*/
|
||||||
export class TimeoutError extends CustomError {}
|
export class TimeoutError extends CustomError {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AbortError is emitted whenever certain operations are terminated due to
|
||||||
|
* an abort request.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Example operations are {@link Page.waitForSelector | page.waitForSelector}.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export class AbortError extends CustomError {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProtocolError is emitted whenever there is an error from the protocol.
|
* ProtocolError is emitted whenever there is an error from the protocol.
|
||||||
*
|
*
|
||||||
|
@ -72,6 +72,10 @@ export interface WaitForSelectorOptions {
|
|||||||
* @defaultValue `30_000` (30 seconds)
|
* @defaultValue `30_000` (30 seconds)
|
||||||
*/
|
*/
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
/**
|
||||||
|
* Provide an abort controller to cancel a waitForSelector call.
|
||||||
|
*/
|
||||||
|
abortController?: AbortController;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,6 +435,7 @@ export class IsolatedWorld {
|
|||||||
polling?: 'raf' | 'mutation' | number;
|
polling?: 'raf' | 'mutation' | number;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
root?: ElementHandle<Node>;
|
root?: ElementHandle<Node>;
|
||||||
|
abortController?: AbortController;
|
||||||
} = {},
|
} = {},
|
||||||
...args: Params
|
...args: Params
|
||||||
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
@ -438,6 +443,7 @@ export class IsolatedWorld {
|
|||||||
polling = 'raf',
|
polling = 'raf',
|
||||||
timeout = this.#timeoutSettings.timeout(),
|
timeout = this.#timeoutSettings.timeout(),
|
||||||
root,
|
root,
|
||||||
|
abortController,
|
||||||
} = options;
|
} = options;
|
||||||
if (typeof polling === 'number' && polling < 0) {
|
if (typeof polling === 'number' && polling < 0) {
|
||||||
throw new Error('Cannot poll with non-positive interval');
|
throw new Error('Cannot poll with non-positive interval');
|
||||||
@ -448,6 +454,7 @@ export class IsolatedWorld {
|
|||||||
polling,
|
polling,
|
||||||
root,
|
root,
|
||||||
timeout,
|
timeout,
|
||||||
|
abortController,
|
||||||
},
|
},
|
||||||
pageFunction as unknown as
|
pageFunction as unknown as
|
||||||
| ((...args: unknown[]) => Promise<Awaited<ReturnType<Func>>>)
|
| ((...args: unknown[]) => Promise<Awaited<ReturnType<Func>>>)
|
||||||
|
@ -20,6 +20,7 @@ import {assert} from '../util/assert.js';
|
|||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
import {interpolateFunction, stringifyFunction} from '../util/Function.js';
|
import {interpolateFunction, stringifyFunction} from '../util/Function.js';
|
||||||
|
|
||||||
|
import {AbortError} from './Errors.js';
|
||||||
import type {Frame} from './Frame.js';
|
import type {Frame} from './Frame.js';
|
||||||
import {transposeIterableHandle} from './HandleIterator.js';
|
import {transposeIterableHandle} from './HandleIterator.js';
|
||||||
import type {WaitForSelectorOptions} from './IsolatedWorld.js';
|
import type {WaitForSelectorOptions} from './IsolatedWorld.js';
|
||||||
@ -166,9 +167,13 @@ export class QueryHandler {
|
|||||||
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame);
|
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
const {visible = false, hidden = false, timeout} = options;
|
const {visible = false, hidden = false, timeout, abortController} = options;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (options.abortController?.signal.aborted) {
|
||||||
|
throw new AbortError('QueryHander.waitFor has been aborted.');
|
||||||
|
}
|
||||||
|
|
||||||
const handle = await frame.worlds[PUPPETEER_WORLD].waitForFunction(
|
const handle = await frame.worlds[PUPPETEER_WORLD].waitForFunction(
|
||||||
async (PuppeteerUtil, query, selector, root, visible) => {
|
async (PuppeteerUtil, query, selector, root, visible) => {
|
||||||
const querySelector = PuppeteerUtil.createFunction(
|
const querySelector = PuppeteerUtil.createFunction(
|
||||||
@ -185,6 +190,7 @@ export class QueryHandler {
|
|||||||
polling: visible || hidden ? 'raf' : 'mutation',
|
polling: visible || hidden ? 'raf' : 'mutation',
|
||||||
root: element,
|
root: element,
|
||||||
timeout,
|
timeout,
|
||||||
|
abortController,
|
||||||
},
|
},
|
||||||
LazyArg.create(context => {
|
LazyArg.create(context => {
|
||||||
return context.puppeteerUtil;
|
return context.puppeteerUtil;
|
||||||
@ -195,6 +201,11 @@ export class QueryHandler {
|
|||||||
visible ? true : hidden ? false : undefined
|
visible ? true : hidden ? false : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (options.abortController?.signal.aborted) {
|
||||||
|
await handle.dispose();
|
||||||
|
throw new AbortError('QueryHander.waitFor has been aborted.');
|
||||||
|
}
|
||||||
|
|
||||||
if (!(handle instanceof ElementHandle)) {
|
if (!(handle instanceof ElementHandle)) {
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return null;
|
return null;
|
||||||
|
@ -20,7 +20,7 @@ import type {Poller} from '../injected/Poller.js';
|
|||||||
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
||||||
import {stringifyFunction} from '../util/Function.js';
|
import {stringifyFunction} from '../util/Function.js';
|
||||||
|
|
||||||
import {TimeoutError} from './Errors.js';
|
import {TimeoutError, AbortError} from './Errors.js';
|
||||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||||
import {LazyArg} from './LazyArg.js';
|
import {LazyArg} from './LazyArg.js';
|
||||||
import {HandleFor} from './types.js';
|
import {HandleFor} from './types.js';
|
||||||
@ -32,6 +32,7 @@ export interface WaitTaskOptions {
|
|||||||
polling: 'raf' | 'mutation' | number;
|
polling: 'raf' | 'mutation' | number;
|
||||||
root?: ElementHandle<Node>;
|
root?: ElementHandle<Node>;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
|
abortController?: AbortController;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,6 +51,7 @@ export class WaitTask<T = unknown> {
|
|||||||
#result = createDeferredPromise<HandleFor<T>>();
|
#result = createDeferredPromise<HandleFor<T>>();
|
||||||
|
|
||||||
#poller?: JSHandle<Poller<T>>;
|
#poller?: JSHandle<Poller<T>>;
|
||||||
|
#abortController?: AbortController;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
world: IsolatedWorld,
|
world: IsolatedWorld,
|
||||||
@ -60,6 +62,16 @@ export class WaitTask<T = unknown> {
|
|||||||
this.#world = world;
|
this.#world = world;
|
||||||
this.#polling = options.polling;
|
this.#polling = options.polling;
|
||||||
this.#root = options.root;
|
this.#root = options.root;
|
||||||
|
this.#abortController = options.abortController;
|
||||||
|
this.#abortController?.signal?.addEventListener(
|
||||||
|
'abort',
|
||||||
|
() => {
|
||||||
|
this.terminate(new AbortError('WaitTask has been aborted.'));
|
||||||
|
},
|
||||||
|
{
|
||||||
|
once: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
switch (typeof fn) {
|
switch (typeof fn) {
|
||||||
case 'string':
|
case 'string':
|
||||||
|
@ -380,6 +380,18 @@ describe('waittask specs', function () {
|
|||||||
await frame.waitForSelector('div');
|
await frame.waitForSelector('div');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be cancellable', async () => {
|
||||||
|
const {page, server} = getTestState();
|
||||||
|
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const task = page.waitForSelector('wrong', {
|
||||||
|
abortController,
|
||||||
|
});
|
||||||
|
abortController.abort();
|
||||||
|
expect(task).rejects.toThrow(/aborted/);
|
||||||
|
});
|
||||||
|
|
||||||
it('should work with removed MutationObserver', async () => {
|
it('should work with removed MutationObserver', async () => {
|
||||||
const {page} = getTestState();
|
const {page} = getTestState();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user