chore: implement simple BiDi ElementHandle (#9753)

This commit is contained in:
Nikolay Vitkov 2023-03-01 11:09:17 +01:00 committed by GitHub
parent 232873ae76
commit 004a99aaef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 177 additions and 112 deletions

View File

@ -8,10 +8,10 @@ sidebar_label: ElementHandle.asElement
```typescript
class ElementHandle {
asElement(): ElementHandle<ElementType> | null;
asElement(): ElementHandle<ElementType>;
}
```
**Returns:**
[ElementHandle](./puppeteer.elementhandle.md)&lt;ElementType&gt; \| null
[ElementHandle](./puppeteer.elementhandle.md)&lt;ElementType&gt;

View File

@ -25,6 +25,7 @@ import {
ElementFor,
EvaluateFuncWith,
HandleFor,
HandleOr,
NodeFor,
} from '../common/types.js';
import {KeyInput} from '../common/USKeyboardLayout.js';
@ -158,8 +159,108 @@ export class ElementHandle<
/**
* @internal
*/
constructor() {
protected handle;
/**
* @internal
*/
constructor(handle: JSHandle<ElementType>) {
super();
this.handle = handle;
}
/**
* @internal
*/
override get id(): string | undefined {
return this.handle.id;
}
/**
* @internal
*/
override get disposed(): boolean {
return this.handle.disposed;
}
/**
* @internal
*/
override async getProperty<K extends keyof ElementType>(
propertyName: HandleOr<K>
): Promise<HandleFor<ElementType[K]>>;
/**
* @internal
*/
override async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
override async getProperty<K extends keyof ElementType>(
propertyName: HandleOr<K>
): Promise<HandleFor<ElementType[K]>> {
return this.handle.getProperty(propertyName);
}
/**
* @internal
*/
override async getProperties(): Promise<Map<string, JSHandle>> {
return this.handle.getProperties();
}
/**
* @internal
*/
override async evaluate<
Params extends unknown[],
Func extends EvaluateFuncWith<ElementType, Params> = EvaluateFuncWith<
ElementType,
Params
>
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
return this.handle.evaluate(pageFunction, ...args);
}
/**
* @internal
*/
override evaluateHandle<
Params extends unknown[],
Func extends EvaluateFuncWith<ElementType, Params> = EvaluateFuncWith<
ElementType,
Params
>
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
return this.handle.evaluateHandle(pageFunction, ...args);
}
/**
* @internal
*/
override async jsonValue(): Promise<ElementType> {
return this.handle.jsonValue();
}
/**
* @internal
*/
override toString(): string {
return this.handle.toString();
}
/**
* @internal
*/
override async dispose(): Promise<void> {
return await this.handle.dispose();
}
override asElement(): ElementHandle<ElementType> {
return this;
}
/**
@ -468,10 +569,6 @@ export class ElementHandle<
throw new Error('Not implemented');
}
override asElement(): ElementHandle<ElementType> | null {
return this;
}
/**
* Resolves to the content frame for element handles referencing
* iframe nodes, or null otherwise

View File

@ -25,7 +25,6 @@ import {
Point,
PressOptions,
} from '../api/ElementHandle.js';
import {JSHandle} from '../api/JSHandle.js';
import {Page, ScreenshotOptions} from '../api/Page.js';
import {assert} from '../util/assert.js';
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
@ -38,13 +37,7 @@ import {getQueryHandlerAndSelector} from './GetQueryHandler.js';
import {WaitForSelectorOptions} from './IsolatedWorld.js';
import {CDPJSHandle} from './JSHandle.js';
import {CDPPage} from './Page.js';
import {
ElementFor,
EvaluateFuncWith,
HandleFor,
HandleOr,
NodeFor,
} from './types.js';
import {ElementFor, EvaluateFuncWith, HandleFor, NodeFor} from './types.js';
import {KeyInput} from './USKeyboardLayout.js';
import {debugError, isString} from './util.js';
@ -69,15 +62,14 @@ export class CDPElementHandle<
ElementType extends Node = Element
> extends ElementHandle<ElementType> {
#frame: Frame;
#jsHandle: CDPJSHandle<ElementType>;
declare handle: CDPJSHandle<ElementType>;
constructor(
context: ExecutionContext,
remoteObject: Protocol.Runtime.RemoteObject,
frame: Frame
) {
super();
this.#jsHandle = new CDPJSHandle(context, remoteObject);
super(new CDPJSHandle(context, remoteObject));
this.#frame = frame;
}
@ -85,48 +77,18 @@ export class CDPElementHandle<
* @internal
*/
override executionContext(): ExecutionContext {
return this.#jsHandle.executionContext();
return this.handle.executionContext();
}
/**
* @internal
*/
override get client(): CDPSession {
return this.#jsHandle.client;
}
override get id(): string | undefined {
return this.#jsHandle.id;
return this.handle.client;
}
override remoteObject(): Protocol.Runtime.RemoteObject {
return this.#jsHandle.remoteObject();
}
override async evaluate<
Params extends unknown[],
Func extends EvaluateFuncWith<ElementType, Params> = EvaluateFuncWith<
ElementType,
Params
>
>(
pageFunction: Func | string,
...args: Params
): Promise<Awaited<ReturnType<Func>>> {
return this.executionContext().evaluate(pageFunction, this, ...args);
}
override evaluateHandle<
Params extends unknown[],
Func extends EvaluateFuncWith<ElementType, Params> = EvaluateFuncWith<
ElementType,
Params
>
>(
pageFunction: Func | string,
...args: Params
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
return this.executionContext().evaluateHandle(pageFunction, this, ...args);
return this.handle.remoteObject();
}
get #frameManager(): FrameManager {
@ -141,40 +103,6 @@ export class CDPElementHandle<
return this.#frame;
}
override get disposed(): boolean {
return this.#jsHandle.disposed;
}
override async getProperty<K extends keyof ElementType>(
propertyName: HandleOr<K>
): Promise<HandleFor<ElementType[K]>>;
override async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
override async getProperty<K extends keyof ElementType>(
propertyName: HandleOr<K>
): Promise<HandleFor<ElementType[K]>> {
return this.#jsHandle.getProperty(propertyName);
}
override async getProperties(): Promise<Map<string, JSHandle>> {
return this.#jsHandle.getProperties();
}
override asElement(): CDPElementHandle<ElementType> | null {
return this;
}
override async jsonValue(): Promise<ElementType> {
return this.#jsHandle.jsonValue();
}
override toString(): string {
return this.#jsHandle.toString();
}
override async dispose(): Promise<void> {
return await this.#jsHandle.dispose();
}
override async $<Selector extends string>(
selector: Selector
): Promise<CDPElementHandle<NodeFor<Selector>> | null> {

View File

@ -22,6 +22,7 @@ import {EvaluateFunc, HandleFor} from '../types.js';
import {isString} from '../util.js';
import {Connection} from './Connection.js';
import {ElementHandle} from './ElementHandle.js';
import {JSHandle} from './JSHandle.js';
import {BidiSerializer} from './Serializer.js';
@ -131,10 +132,9 @@ export class Context extends EventEmitter {
export function getBidiHandle(
context: Context,
result: Bidi.CommonDataTypes.RemoteValue
): JSHandle {
if ((result.type === 'node' || result.type === 'window') && context) {
// TODO: Implement ElementHandle
return new JSHandle(context, result);
): JSHandle | ElementHandle<Node> {
if (result.type === 'node' || result.type === 'window') {
return new ElementHandle(context, result);
}
return new JSHandle(context, result);
}

View File

@ -0,0 +1,52 @@
/**
* Copyright 2023 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 * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {ElementHandle as BaseElementHandle} from '../../api/ElementHandle.js';
import {Connection} from './Connection.js';
import {Context} from './Context.js';
import {JSHandle} from './JSHandle.js';
/**
* @internal
*/
export class ElementHandle<
ElementType extends Node = Element
> extends BaseElementHandle<ElementType> {
declare handle: JSHandle<ElementType>;
constructor(context: Context, remoteValue: Bidi.CommonDataTypes.RemoteValue) {
super(new JSHandle(context, remoteValue));
}
context(): Context {
return this.handle.context();
}
get connection(): Connection {
return this.handle.connection;
}
get isPrimitiveValue(): boolean {
return this.handle.isPrimitiveValue;
}
remoteValue(): Bidi.CommonDataTypes.RemoteValue {
return this.handle.remoteValue();
}
}

View File

@ -20,7 +20,6 @@ import {Page as PageBase, PageEmittedEvents} from '../../api/Page.js';
import {ConsoleMessage, ConsoleMessageLocation} from '../ConsoleMessage.js';
import {EvaluateFunc, HandleFor} from '../types.js';
import {Connection} from './Connection.js';
import {Context, getBidiHandle} from './Context.js';
import {BidiSerializer} from './Serializer.js';
@ -38,10 +37,9 @@ export class Page extends PageBase {
super();
this.#context = context;
// TODO: Investigate an implementation similar to CDPSession
this.connection.send('session.subscribe', {
this.#context.connection.send('session.subscribe', {
events: this.#subscribedEvents,
contexts: [this.contextId],
contexts: [this.#context.id],
});
this.#context.on('log.entryAdded', this.#boundOnLogEntryAdded);
@ -85,26 +83,18 @@ export class Page extends PageBase {
}
override async close(): Promise<void> {
await this.connection.send('session.unsubscribe', {
await this.#context.connection.send('session.unsubscribe', {
events: this.#subscribedEvents,
contexts: [this.contextId],
contexts: [this.#context.id],
});
await this.connection.send('browsingContext.close', {
context: this.contextId,
await this.#context.connection.send('browsingContext.close', {
context: this.#context.id,
});
this.#context.off('log.entryAdded', this.#boundOnLogEntryAdded);
}
get connection(): Connection {
return this.#context.connection;
}
get contextId(): string {
return this.#context.id;
}
override async evaluateHandle<
Params extends unknown[],
Func extends EvaluateFunc<Params> = EvaluateFunc<Params>

View File

@ -19,6 +19,7 @@ import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {debugError, isDate, isPlainObject, isRegExp} from '../util.js';
import {Context} from './Context.js';
import {ElementHandle} from './ElementHandle.js';
import {JSHandle} from './JSHandle.js';
/**
@ -149,7 +150,10 @@ export class BidiSerializer {
context: Context
): Bidi.CommonDataTypes.LocalOrRemoteValue {
// TODO: See use case of LazyArgs
const objectHandle = arg && arg instanceof JSHandle ? arg : null;
const objectHandle =
arg && (arg instanceof JSHandle || arg instanceof ElementHandle)
? arg
: null;
if (objectHandle) {
if (objectHandle.context() !== context) {
throw new Error(

View File

@ -1775,12 +1775,6 @@
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[jshandle.spec] JSHandle JSHandle.asElement should work",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["webDriverBiDi"],
"expectations": ["FAIL"]
},
{
"testIdPattern": "[jshandle.spec] JSHandle JSHandle.asElement should return ElementHandle for TextNodes",
"platforms": ["darwin", "linux", "win32"],