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 { class ElementHandle {
waitForSelector<Selector extends string>( waitForSelector<Selector extends string>(
selector: Selector, selector: Selector,
options?: Exclude<WaitForSelectorOptions, 'root'> options?: WaitForSelectorOptions
): Promise<ElementHandle<NodeFor<Selector>> | null>; ): Promise<ElementHandle<NodeFor<Selector>> | null>;
} }
``` ```
## Parameters ## Parameters
| Parameter | Type | Description | | Parameter | Type | Description |
| --------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------- | | --------- | --------------------------------------------------------------- | ----------------------------------------------------------- |
| selector | Selector | The selector to query and wait for. | | 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. | | options | [WaitForSelectorOptions](./puppeteer.waitforselectoroptions.md) | <i>(Optional)</i> Options for customizing waiting behavior. |
**Returns:** **Returns:**

View File

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

View File

@ -12,9 +12,8 @@ export interface WaitForSelectorOptions
## Properties ## Properties
| Property | Modifiers | Type | Description | | 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. | | [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> |
| [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. |
| [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 {assert} from '../util/assert.js';
import {CDPSession} from './Connection.js'; import {CDPSession} from './Connection.js';
import {ElementHandle} from './ElementHandle.js'; import {ElementHandle} from './ElementHandle.js';
import { import {Frame} from './Frame.js';
IsolatedWorld, import {MAIN_WORLD, PageBinding, PUPPETEER_WORLD} from './IsolatedWorld.js';
PageBinding,
WaitForSelectorOptions,
} from './IsolatedWorld.js';
import {InternalQueryHandler} from './QueryHandler.js'; import {InternalQueryHandler} from './QueryHandler.js';
async function queryAXTree( async function queryAXTree(
@ -89,52 +86,86 @@ function parseAriaSelector(selector: string): ARIAQueryOption {
return queryOptions; return queryOptions;
} }
const queryOne = async ( const queryOneId = async (element: ElementHandle<Node>, selector: string) => {
element: ElementHandle<Node>,
selector: string
): Promise<ElementHandle<Node> | null> => {
const exeCtx = element.executionContext();
const {name, role} = parseAriaSelector(selector); 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) { if (!res[0] || !res[0].backendDOMNodeId) {
return null; return null;
} }
return (await exeCtx._world!.adoptBackendNode( return res[0].backendDOMNodeId;
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>; )) as ElementHandle<Node>;
}; };
const waitFor = async ( const waitFor: InternalQueryHandler['waitFor'] = async (
isolatedWorld: IsolatedWorld, elementOrFrame,
selector: string, selector,
options: WaitForSelectorOptions options
): Promise<ElementHandle<Element> | null> => { ) => {
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 = { const binding: PageBinding = {
name: 'ariaQuerySelector', name: 'ariaQuerySelector',
pptrFunction: async (selector: string) => { pptrFunction: async (selector: string) => {
const root = options.root || (await isolatedWorld.document()); const id = await queryOneId(
const element = await queryOne(root, selector); element || (await frame.worlds[PUPPETEER_WORLD].document()),
return element; 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) => { (_: Element, selector: string) => {
return ( return (
globalThis as unknown as { globalThis as unknown as {
ariaQuerySelector(selector: string): void; ariaQuerySelector(selector: string): Node | null;
} }
).ariaQuerySelector(selector); ).ariaQuerySelector(selector);
}, },
element,
selector, selector,
options, options,
binding 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 ( const queryAll: InternalQueryHandler['queryAll'] = async (
element: ElementHandle<Node>, element,
selector: string selector
): Promise<Array<ElementHandle<Node>>> => { ) => {
const exeCtx = element.executionContext(); const exeCtx = element.executionContext();
const {name, role} = parseAriaSelector(selector); const {name, role} = parseAriaSelector(selector);
const res = await queryAXTree(exeCtx._client, element, name, role); 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 {ExecutionContext} from './ExecutionContext.js';
import {Frame} from './Frame.js'; import {Frame} from './Frame.js';
import {FrameManager} from './FrameManager.js'; import {FrameManager} from './FrameManager.js';
import { import {WaitForSelectorOptions} from './IsolatedWorld.js';
MAIN_WORLD,
PUPPETEER_WORLD,
WaitForSelectorOptions,
} from './IsolatedWorld.js';
import { import {
BoundingBox, BoundingBox,
BoxModel, BoxModel,
@ -310,26 +306,16 @@ export class ElementHandle<
*/ */
async waitForSelector<Selector extends string>( async waitForSelector<Selector extends string>(
selector: Selector, selector: Selector,
options: Exclude<WaitForSelectorOptions, 'root'> = {} options: WaitForSelectorOptions = {}
): Promise<ElementHandle<NodeFor<Selector>> | null> { ): Promise<ElementHandle<NodeFor<Selector>> | null> {
const frame = this.#frame; const {updatedSelector, queryHandler} =
const adoptedRoot = await frame.worlds[PUPPETEER_WORLD].adoptHandle(this); getQueryHandlerAndSelector(selector);
const handle = await frame.worlds[PUPPETEER_WORLD].waitForSelector( assert(queryHandler.waitFor, 'Query handler does not support waiting');
selector, return (await queryHandler.waitFor(
{ this,
...options, updatedSelector,
root: adoptedRoot, options
} )) as ElementHandle<NodeFor<Selector>> | null;
);
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;
} }
/** /**

View File

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

View File

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

View File

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