fix!: remove root from WaitForSelectorOptions (#8848)

This commit is contained in:
jrandolf 2022-08-26 12:55:30 +02:00 committed by GitHub
parent 498fbf924c
commit 1155c8eac8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 152 additions and 149 deletions

View File

@ -14,17 +14,17 @@ Unlike [Frame.waitForSelector()](./puppeteer.frame.waitforselector.md), this met
class ElementHandle {
waitForSelector<Selector extends string>(
selector: Selector,
options?: Exclude<WaitForSelectorOptions, 'root'>
options?: WaitForSelectorOptions
): Promise<ElementHandle<NodeFor<Selector>> | null>;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| selector | Selector | The selector to query and wait for. |
| options | Exclude&lt;[WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md), 'root'&gt; | <i>(Optional)</i> Options for customizing waiting behavior. |
| Parameter | Type | Description |
| --------- | --------------------------------------------------------------- | ----------------------------------------------------------- |
| selector | Selector | The selector to query and wait for. |
| options | [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | <i>(Optional)</i> Options for customizing waiting behavior. |
**Returns:**

View File

@ -34,17 +34,17 @@ const puppeteer = require('puppeteer');
class Page {
waitForSelector<Selector extends string>(
selector: Selector,
options?: Exclude<WaitForSelectorOptions, 'root'>
options?: WaitForSelectorOptions
): Promise<ElementHandle<NodeFor<Selector>> | null>;
}
```
## Parameters
| Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| selector | Selector | A [selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) of an element to wait for |
| options | Exclude&lt;[WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md), 'root'&gt; | <i>(Optional)</i> Optional waiting parameters |
| Parameter | Type | Description |
| --------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| selector | Selector | A [selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) of an element to wait for |
| options | [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | <i>(Optional)</i> Optional waiting parameters |
**Returns:**

View File

@ -12,9 +12,8 @@ export interface WaitForSelectorOptions
## Properties
| Property | Modifiers | Type | Description |
| --------------------------------------------------------- | --------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [hidden?](./puppeteer.waitforselectoroptions.hidden.md) | | boolean | <i>(Optional)</i> 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. |
| [root?](./puppeteer.waitforselectoroptions.root.md) | | [ElementHandle](./puppeteer.elementhandle.md)&lt;Node&gt; | <i>(Optional)</i> |
| [timeout?](./puppeteer.waitforselectoroptions.timeout.md) | | number | <p><i>(Optional)</i> 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> |
| [visible?](./puppeteer.waitforselectoroptions.visible.md) | | boolean | <i>(Optional)</i> 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. |
| Property | Modifiers | Type | Description |
| --------------------------------------------------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [hidden?](./puppeteer.waitforselectoroptions.hidden.md) | | boolean | <i>(Optional)</i> 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. |
| [timeout?](./puppeteer.waitforselectoroptions.timeout.md) | | number | <p><i>(Optional)</i> 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> |
| [visible?](./puppeteer.waitforselectoroptions.visible.md) | | boolean | <i>(Optional)</i> 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. |

View File

@ -1,17 +0,0 @@
---
sidebar_label: WaitForSelectorOptions.root
---
# WaitForSelectorOptions.root property
> Warning: This API is now obsolete.
>
> Do not use. Use the [ElementHandle.waitForSelector()](./puppeteer.elementhandle.waitforselector.md)
**Signature:**
```typescript
interface WaitForSelectorOptions {
root?: ElementHandle<Node>;
}
```

View File

@ -18,11 +18,8 @@ import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {CDPSession} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
import {
IsolatedWorld,
PageBinding,
WaitForSelectorOptions,
} from './IsolatedWorld.js';
import {Frame} from './Frame.js';
import {MAIN_WORLD, PageBinding, PUPPETEER_WORLD} from './IsolatedWorld.js';
import {InternalQueryHandler} from './QueryHandler.js';
async function queryAXTree(
@ -89,52 +86,86 @@ function parseAriaSelector(selector: string): ARIAQueryOption {
return queryOptions;
}
const queryOne = async (
element: ElementHandle<Node>,
selector: string
): Promise<ElementHandle<Node> | null> => {
const exeCtx = element.executionContext();
const queryOneId = async (element: ElementHandle<Node>, selector: string) => {
const {name, role} = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role);
const res = await queryAXTree(element.client, element, name, role);
if (!res[0] || !res[0].backendDOMNodeId) {
return null;
}
return (await exeCtx._world!.adoptBackendNode(
res[0].backendDOMNodeId
return res[0].backendDOMNodeId;
};
const queryOne: InternalQueryHandler['queryOne'] = async (
element,
selector
) => {
const id = await queryOneId(element, selector);
if (!id) {
return null;
}
return (await element.frame.worlds[MAIN_WORLD].adoptBackendNode(
id
)) as ElementHandle<Node>;
};
const waitFor = async (
isolatedWorld: IsolatedWorld,
selector: string,
options: WaitForSelectorOptions
): Promise<ElementHandle<Element> | null> => {
const waitFor: InternalQueryHandler['waitFor'] = async (
elementOrFrame,
selector,
options
) => {
let frame: Frame;
let element: ElementHandle<Node> | undefined;
if (elementOrFrame instanceof Frame) {
frame = elementOrFrame;
} else {
frame = elementOrFrame.frame;
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame);
}
const binding: PageBinding = {
name: 'ariaQuerySelector',
pptrFunction: async (selector: string) => {
const root = options.root || (await isolatedWorld.document());
const element = await queryOne(root, selector);
return element;
const id = await queryOneId(
element || (await frame.worlds[PUPPETEER_WORLD].document()),
selector
);
if (!id) {
return null;
}
return (await frame.worlds[PUPPETEER_WORLD].adoptBackendNode(
id
)) as ElementHandle<Node>;
},
};
return (await isolatedWorld._waitForSelectorInPage(
const result = await frame.worlds[PUPPETEER_WORLD]._waitForSelectorInPage(
(_: Element, selector: string) => {
return (
globalThis as unknown as {
ariaQuerySelector(selector: string): void;
ariaQuerySelector(selector: string): Node | null;
}
).ariaQuerySelector(selector);
},
element,
selector,
options,
binding
)) as ElementHandle<Element> | null;
);
if (element) {
await element.dispose();
}
if (!result) {
return null;
}
if (!(result instanceof ElementHandle)) {
await result.dispose();
return null;
}
return result.frame.worlds[MAIN_WORLD].transferHandle(result);
};
const queryAll = async (
element: ElementHandle<Node>,
selector: string
): Promise<Array<ElementHandle<Node>>> => {
const queryAll: InternalQueryHandler['queryAll'] = async (
element,
selector
) => {
const exeCtx = element.executionContext();
const {name, role} = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role);

View File

@ -3,11 +3,7 @@ import {assert} from '../util/assert.js';
import {ExecutionContext} from './ExecutionContext.js';
import {Frame} from './Frame.js';
import {FrameManager} from './FrameManager.js';
import {
MAIN_WORLD,
PUPPETEER_WORLD,
WaitForSelectorOptions,
} from './IsolatedWorld.js';
import {WaitForSelectorOptions} from './IsolatedWorld.js';
import {
BoundingBox,
BoxModel,
@ -310,26 +306,16 @@ export class ElementHandle<
*/
async waitForSelector<Selector extends string>(
selector: Selector,
options: Exclude<WaitForSelectorOptions, 'root'> = {}
options: WaitForSelectorOptions = {}
): Promise<ElementHandle<NodeFor<Selector>> | null> {
const frame = this.#frame;
const adoptedRoot = await frame.worlds[PUPPETEER_WORLD].adoptHandle(this);
const handle = await frame.worlds[PUPPETEER_WORLD].waitForSelector(
selector,
{
...options,
root: adoptedRoot,
}
);
await adoptedRoot.dispose();
if (!handle) {
return null;
}
const result = (await frame.worlds[MAIN_WORLD].adoptHandle(
handle
)) as ElementHandle<NodeFor<Selector>>;
await handle.dispose();
return result;
const {updatedSelector, queryHandler} =
getQueryHandlerAndSelector(selector);
assert(queryHandler.waitFor, 'Query handler does not support waiting');
return (await queryHandler.waitFor(
this,
updatedSelector,
options
)) as ElementHandle<NodeFor<Selector>> | null;
}
/**

View File

@ -1,4 +1,5 @@
import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {isErrorLike} from '../util/ErrorLike.js';
import {CDPSession} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
@ -15,6 +16,7 @@ import {
} from './IsolatedWorld.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
import {Page} from './Page.js';
import {getQueryHandlerAndSelector} from './QueryHandler.js';
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
/**
@ -579,18 +581,14 @@ export class Frame {
selector: Selector,
options: WaitForSelectorOptions = {}
): Promise<ElementHandle<NodeFor<Selector>> | null> {
const handle = await this.worlds[PUPPETEER_WORLD].waitForSelector(
selector,
const {updatedSelector, queryHandler} =
getQueryHandlerAndSelector(selector);
assert(queryHandler.waitFor, 'Query handler does not support waiting');
return (await queryHandler.waitFor(
this,
updatedSelector,
options
);
if (!handle) {
return null;
}
const mainHandle = (await this.worlds[MAIN_WORLD].adoptHandle(
handle
)) as ElementHandle<NodeFor<Selector>>;
await handle.dispose();
return mainHandle;
)) as ElementHandle<NodeFor<Selector>> | null;
}
/**

View File

@ -16,16 +16,19 @@
import {Protocol} from 'devtools-protocol';
import {assert} from '../util/assert.js';
import {
createDeferredPromise,
DeferredPromise,
} from '../util/DeferredPromise.js';
import {CDPSession} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
import {TimeoutError} from './Errors.js';
import {ExecutionContext} from './ExecutionContext.js';
import {FrameManager} from './FrameManager.js';
import {Frame} from './Frame.js';
import {FrameManager} from './FrameManager.js';
import {MouseButton} from './Input.js';
import {JSHandle} from './JSHandle.js';
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
import {getQueryHandlerAndSelector} from './QueryHandler.js';
import {TimeoutSettings} from './TimeoutSettings.js';
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
import {
@ -37,10 +40,6 @@ import {
makePredicateString,
pageBindingInitString,
} from './util.js';
import {
createDeferredPromise,
DeferredPromise,
} from '../util/DeferredPromise.js';
// predicateQueryHandler and checkWaitForOptions are declared here so that
// TypeScript knows about them when used in the predicate function below.
@ -80,10 +79,6 @@ export interface WaitForSelectorOptions {
* @defaultValue `30000` (30 seconds)
*/
timeout?: number;
/**
* @deprecated Do not use. Use the {@link ElementHandle.waitForSelector}
*/
root?: ElementHandle<Node>;
}
/**
@ -122,7 +117,7 @@ export interface IsolatedWorldChart {
*/
export class IsolatedWorld {
#frame: Frame;
#documentPromise: Promise<ElementHandle<Document>> | null = null;
#document?: ElementHandle<Document>;
#contextPromise: DeferredPromise<ExecutionContext> = createDeferredPromise();
#detached = false;
@ -169,7 +164,7 @@ export class IsolatedWorld {
}
clearContext(): void {
this.#documentPromise = null;
this.#document = undefined;
this.#contextPromise = createDeferredPromise();
}
@ -248,15 +243,14 @@ export class IsolatedWorld {
}
async document(): Promise<ElementHandle<Document>> {
if (this.#documentPromise) {
return this.#documentPromise;
if (this.#document) {
return this.#document;
}
this.#documentPromise = this.executionContext().then(async context => {
return await context.evaluateHandle(() => {
return document;
});
const context = await this.executionContext();
this.#document = await context.evaluateHandle(() => {
return document;
});
return this.#documentPromise;
return this.#document;
}
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
@ -294,20 +288,6 @@ export class IsolatedWorld {
return document.$$eval(selector, pageFunction, ...args);
}
async waitForSelector<Selector extends string>(
selector: Selector,
options: WaitForSelectorOptions
): Promise<ElementHandle<NodeFor<Selector>> | null> {
const {updatedSelector, queryHandler} =
getQueryHandlerAndSelector(selector);
assert(queryHandler.waitFor, 'Query handler does not support waiting');
return (await queryHandler.waitFor(
this,
updatedSelector,
options
)) as ElementHandle<NodeFor<Selector>> | null;
}
async content(): Promise<string> {
return await this.evaluate(() => {
let retVal = '';
@ -707,10 +687,11 @@ export class IsolatedWorld {
async _waitForSelectorInPage(
queryOne: Function,
root: ElementHandle<Node> | undefined,
selector: string,
options: WaitForSelectorOptions,
binding?: PageBinding
): Promise<ElementHandle<Node> | null> {
): Promise<JSHandle<unknown> | null> {
const {
visible: waitForVisible = false,
hidden: waitForHidden = false,
@ -738,16 +719,10 @@ export class IsolatedWorld {
timeout,
args: [selector, waitForVisible, waitForHidden],
binding,
root: options.root,
root,
};
const waitTask = new WaitTask(waitTaskOptions);
const jsHandle = await waitTask.promise;
const elementHandle = jsHandle.asElement();
if (!elementHandle) {
await jsHandle.dispose();
return null;
}
return elementHandle;
return waitTask.promise;
}
waitForFunction(
@ -798,6 +773,12 @@ export class IsolatedWorld {
});
return (await this.adoptBackendNode(nodeInfo.node.backendNodeId)) as T;
}
async transferHandle<T extends JSHandle<Node>>(handle: T): Promise<T> {
const result = await this.adoptHandle(handle);
await handle.dispose();
return result;
}
}
/**

View File

@ -3396,7 +3396,7 @@ export class Page extends EventEmitter {
*/
async waitForSelector<Selector extends string>(
selector: Selector,
options: Exclude<WaitForSelectorOptions, 'root'> = {}
options: WaitForSelectorOptions = {}
): Promise<ElementHandle<NodeFor<Selector>> | null> {
return await this.mainFrame().waitForSelector(selector, options);
}

View File

@ -16,7 +16,12 @@
import {ariaHandler} from './AriaQueryHandler.js';
import {ElementHandle} from './ElementHandle.js';
import {IsolatedWorld, WaitForSelectorOptions} from './IsolatedWorld.js';
import {Frame} from './Frame.js';
import {
MAIN_WORLD,
PUPPETEER_WORLD,
WaitForSelectorOptions,
} from './IsolatedWorld.js';
/**
* @public
@ -58,11 +63,9 @@ export interface InternalQueryHandler {
/**
* Waits until a single node appears for a given selector and
* {@link ElementHandle}.
*
* Akin to {@link Window.prototype.querySelectorAll}.
*/
waitFor?: (
isolatedWorld: IsolatedWorld,
elementOrFrame: ElementHandle<Node> | Frame,
selector: string,
options: WaitForSelectorOptions
) => Promise<ElementHandle<Node> | null>;
@ -84,12 +87,34 @@ function internalizeCustomQueryHandler(
await jsHandle.dispose();
return null;
};
internalHandler.waitFor = (
domWorld: IsolatedWorld,
selector: string,
options: WaitForSelectorOptions
) => {
return domWorld._waitForSelectorInPage(queryOne, selector, options);
internalHandler.waitFor = async (elementOrFrame, selector, options) => {
let frame: Frame;
let element: ElementHandle<Node> | undefined;
if (elementOrFrame instanceof Frame) {
frame = elementOrFrame;
} else {
frame = elementOrFrame.frame;
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(
elementOrFrame
);
}
const result = await frame.worlds[PUPPETEER_WORLD]._waitForSelectorInPage(
queryOne,
element,
selector,
options
);
if (element) {
await element.dispose();
}
if (!result) {
return null;
}
if (!(result instanceof ElementHandle)) {
await result.dispose();
return null;
}
return frame.worlds[MAIN_WORLD].transferHandle(result);
};
}