chore: implement type signatures for internal Locator methods (#10603)

This commit is contained in:
jrandolf 2023-07-20 18:44:02 +02:00 committed by GitHub
parent 44712d1e6e
commit 0603f71f77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 35 deletions

View File

@ -73,7 +73,7 @@ import type {
} from './Frame.js'; } from './Frame.js';
import {Keyboard, KeyboardTypeOptions, Mouse, Touchscreen} from './Input.js'; import {Keyboard, KeyboardTypeOptions, Mouse, Touchscreen} from './Input.js';
import type {JSHandle} from './JSHandle.js'; import type {JSHandle} from './JSHandle.js';
import {Locator, NodeLocator} from './locators/locators.js'; import {Locator, NodeLocator, UnionLocatorOf} from './locators/locators.js';
/** /**
* @public * @public
@ -842,8 +842,10 @@ export class Page extends EventEmitter {
* *
* @internal * @internal
*/ */
locatorRace(locators: Array<Locator<Node>>): Locator<Node> { locatorRace<Locators extends Array<Locator<unknown>>>(
return Locator.race(locators); locators: Locators
): Locator<UnionLocatorOf<Locators>> {
return Locator.race(locators as Array<Locator<UnionLocatorOf<Locators>>>);
} }
/** /**

View File

@ -1,5 +1,5 @@
import {TimeoutError} from '../../common/Errors.js'; import {TimeoutError} from '../../common/Errors.js';
import {HandleFor, NodeFor} from '../../common/types.js'; import {Awaitable, HandleFor, NodeFor} from '../../common/types.js';
import {debugError} from '../../common/util.js'; import {debugError} from '../../common/util.js';
import {isErrorLike} from '../../util/ErrorLike.js'; import {isErrorLike} from '../../util/ErrorLike.js';
import {BoundingBox, ElementHandle} from '../ElementHandle.js'; import {BoundingBox, ElementHandle} from '../ElementHandle.js';
@ -93,11 +93,11 @@ export class NodeLocator<T extends Node> extends Locator<T> {
/** /**
* Retries the `fn` until a truthy result is returned. * Retries the `fn` until a truthy result is returned.
*/ */
async #waitForFunction( async #waitForFunction<T>(
fn: (signal: AbortSignal) => unknown, fn: (signal: AbortSignal) => Awaitable<T>,
signal?: AbortSignal, signal?: AbortSignal,
timeout = CONDITION_TIMEOUT timeout = CONDITION_TIMEOUT
): Promise<void> { ): Promise<T> {
let isActive = true; let isActive = true;
let controller: AbortController; let controller: AbortController;
// If the loop times out, we abort only the last iteration's controller. // If the loop times out, we abort only the last iteration's controller.
@ -121,10 +121,8 @@ export class NodeLocator<T extends Node> extends Locator<T> {
controller = new AbortController(); controller = new AbortController();
try { try {
const result = await fn(controller.signal); const result = await fn(controller.signal);
if (result) { clearTimeout(timeoutId);
clearTimeout(timeoutId); return result;
return;
}
} catch (err) { } catch (err) {
if (isErrorLike(err)) { if (isErrorLike(err)) {
debugError(err); debugError(err);
@ -277,11 +275,11 @@ export class NodeLocator<T extends Node> extends Locator<T> {
}, signal); }, signal);
}; };
#run( #run<U>(
action: (el: HandleFor<T>) => Promise<void>, action: (el: HandleFor<T>) => Promise<U>,
signal?: AbortSignal, signal?: AbortSignal,
conditions: Array<ActionCondition<T>> = [] conditions: Array<ActionCondition<T>> = []
) { ): Promise<U> {
const globalConditions = [ const globalConditions = [
...(LOCATOR_CONTEXTS.get(this)?.conditions?.values() ?? []), ...(LOCATOR_CONTEXTS.get(this)?.conditions?.values() ?? []),
] as Array<ActionCondition<T>>; ] as Array<ActionCondition<T>>;
@ -299,23 +297,23 @@ export class NodeLocator<T extends Node> extends Locator<T> {
)) as HandleFor<T> | null; )) as HandleFor<T> | null;
// Retry if no element is found. // Retry if no element is found.
if (!element) { if (!element) {
return false; throw new Error('No element found');
} }
signal?.throwIfAborted();
// 2. Perform action specific checks.
await Promise.all(
allConditions.map(check => {
return check(element, signal);
})
);
signal?.throwIfAborted();
// 3. Perform the action
this.emit(LocatorEmittedEvents.Action);
try { try {
signal?.throwIfAborted(); return await action(element);
// 2. Perform action specific checks. } catch (error) {
await Promise.all(
allConditions.map(check => {
return check(element, signal);
})
);
signal?.throwIfAborted();
// 3. Perform the action
this.emit(LocatorEmittedEvents.Action);
await action(element);
return true;
} finally {
void element.dispose().catch(debugError); void element.dispose().catch(debugError);
throw error;
} }
}, },
signal, signal,
@ -327,9 +325,10 @@ export class NodeLocator<T extends Node> extends Locator<T> {
this: NodeLocator<ElementType>, this: NodeLocator<ElementType>,
options?: Readonly<LocatorClickOptions> options?: Readonly<LocatorClickOptions>
): Promise<void> { ): Promise<void> {
return await this.#run( await this.#run(
async element => { async element => {
await element.click(options); await element.click(options);
void element.dispose().catch(debugError);
}, },
options?.signal, options?.signal,
[ [
@ -347,12 +346,12 @@ export class NodeLocator<T extends Node> extends Locator<T> {
* method is chosen based on the type. contenteditable, selector, inputs are * method is chosen based on the type. contenteditable, selector, inputs are
* supported. * supported.
*/ */
fill<ElementType extends Element>( async fill<ElementType extends Element>(
this: NodeLocator<ElementType>, this: NodeLocator<ElementType>,
value: string, value: string,
options?: Readonly<ActionOptions> options?: Readonly<ActionOptions>
): Promise<void> { ): Promise<void> {
return this.#run( await this.#run(
async element => { async element => {
const input = element as unknown as ElementHandle<HTMLElement>; const input = element as unknown as ElementHandle<HTMLElement>;
const inputType = await input.evaluate(el => { const inputType = await input.evaluate(el => {
@ -439,6 +438,7 @@ export class NodeLocator<T extends Node> extends Locator<T> {
case 'unknown': case 'unknown':
throw new Error(`Element cannot be filled out.`); throw new Error(`Element cannot be filled out.`);
} }
void element.dispose().catch(debugError);
}, },
options?.signal, options?.signal,
[ [
@ -450,13 +450,14 @@ export class NodeLocator<T extends Node> extends Locator<T> {
); );
} }
hover<ElementType extends Element>( async hover<ElementType extends Element>(
this: NodeLocator<ElementType>, this: NodeLocator<ElementType>,
options?: Readonly<ActionOptions> options?: Readonly<ActionOptions>
): Promise<void> { ): Promise<void> {
return this.#run( await this.#run(
async element => { async element => {
await element.hover(); await element.hover();
void element.dispose().catch(debugError);
}, },
options?.signal, options?.signal,
[ [
@ -467,11 +468,11 @@ export class NodeLocator<T extends Node> extends Locator<T> {
); );
} }
scroll<ElementType extends Element>( async scroll<ElementType extends Element>(
this: NodeLocator<ElementType>, this: NodeLocator<ElementType>,
options?: Readonly<LocatorScrollOptions> options?: Readonly<LocatorScrollOptions>
): Promise<void> { ): Promise<void> {
return this.#run( await this.#run(
async element => { async element => {
await element.evaluate( await element.evaluate(
(el, scrollTop, scrollLeft) => { (el, scrollTop, scrollLeft) => {
@ -485,6 +486,7 @@ export class NodeLocator<T extends Node> extends Locator<T> {
options?.scrollTop, options?.scrollTop,
options?.scrollLeft options?.scrollLeft
); );
void element.dispose().catch(debugError);
}, },
options?.signal, options?.signal,
[ [