Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make JSHandle generic #140

Merged
merged 3 commits into from
Dec 6, 2019
Merged

feat: make JSHandle generic #140

merged 3 commits into from
Dec 6, 2019

Conversation

JoelEinbinder
Copy link
Contributor

This makes it so that JSHandles and ElementHandles are aware of what types they point to. As a fun bonus, $eval('input') knows its going to get an HTMLInputElement.

Most of this patch is casting things where previously we just assumed ElementHandles held the right kind of node. This gets us closer to being able to turn on noImplicityAny as well.

#6

const resolved = await this.resolveSelector(selector);
const handle = await this.context.evaluateHandle(
(injected: Injected, selector: string, scope: SelectorRoot | undefined, visible: boolean | undefined) => {
const element = injected.querySelector(selector, scope || document);
(injected: Injected, selector: string, scope?: Node, visible?: boolean) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why scope type has changed? Injected cannot query against a Node, only against some specific types (SelectorRoot).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's true that injected can only query against certain kinds of Nodes, but it is prepared to accept and throw the appropriate error if it is wrong.

We could have ElementHandle check that the Node is a a SelectorRoot, but that would take an extra round trip. So I changed the type of scope to accurately reflect its value.

@@ -144,7 +144,7 @@ export class DOMWorld {
}
}

export class ElementHandle extends js.JSHandle {
export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make the default Element? This will simplify a lot of places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It causes us to need more ElementHandle<Node> instead.

const parsed = this._parseSelector(selector);
let element = root;
let element = root as Element;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem to be true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made it check the type and throw an error before continuing

export type EvaluateOn = <Args extends any[], R>(pageFunction: PageFunctionOn<any, Args, R>, ...args: Boxed<Args>) => Promise<R>;
export type EvaluateHandleOn = <Args extends any[]>(pageFunction: PageFunctionOn<any, Args>, ...args: Boxed<Args>) => Promise<js.JSHandle>;
export type EvaluateHandle = <Args extends any[], R>(pageFunction: PageFunction<Args, R>, ...args: Boxed<Args>) => Promise<Handle<R>>;
export type $Eval<O = string | Selector> = <Args extends any[], R, S extends O>(selector: S, pageFunction: PageFunctionOn<ElementForSelector<S>, Args, R>, ...args: Boxed<Args>) => Promise<R>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why separate S and O?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O is the allowed options, S is the specific string or type that got passed it. It lets me do $eval('input', x => ...) and know that x is HTMLInputElement because S is 'input' not string

src/dom.ts Outdated Show resolved Hide resolved
src/dom.ts Outdated
@@ -242,13 +246,13 @@ export class ElementHandle extends js.JSHandle {
}

async setInputFiles(...files: (string|input.FilePayload)[]) {
const multiple = await this.evaluate((element: HTMLInputElement) => !!element.multiple);
const multiple = await this.evaluate((element: Node) => !!(element as HTMLInputElement).multiple);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's runtime check.

src/dom.ts Outdated
assert(multiple || files.length <= 1, 'Non-multiple file input can only accept single file!');
await this._world.delegate.setInputFiles(this, await input.loadFiles(files));
}

async focus() {
await this.evaluate(element => element.focus());
await this.evaluate((element: Node) => (element as HTMLElement).focus());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's runtime check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -76,7 +76,7 @@ export class JSHandle {
return this._context._delegate.getProperties(this);
}

jsonValue(): Promise<any> {
jsonValue(): Promise<T> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's type the ExecutionContextDelegate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I typed handleJSONValue, but getProperties isn't great to type. You can't make a Map in typescript that has specific types of keys go to specific types of values. We could do it if we were returning plain objects, but maps is a no-go.

@JoelEinbinder JoelEinbinder merged commit 39b22b4 into microsoft:master Dec 6, 2019
@JoelEinbinder JoelEinbinder deleted the element_handle_types branch December 6, 2019 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants