chore: implement mapHandle
and filterHandle
(#10716)
This commit is contained in:
parent
47dfc3578d
commit
0c59e9a1cb
@ -16,13 +16,13 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Observable,
|
Observable,
|
||||||
|
filter,
|
||||||
from,
|
from,
|
||||||
map,
|
map,
|
||||||
mergeMap,
|
mergeMap,
|
||||||
throwIfEmpty,
|
throwIfEmpty,
|
||||||
} from '../../../third_party/rxjs/rxjs.js';
|
} from '../../../third_party/rxjs/rxjs.js';
|
||||||
import {Awaitable, HandleFor} from '../../common/common.js';
|
import {Awaitable, HandleFor} from '../../common/common.js';
|
||||||
import {ElementHandle} from '../ElementHandle.js';
|
|
||||||
|
|
||||||
import {DelegatedLocator} from './DelegatedLocator.js';
|
import {DelegatedLocator} from './DelegatedLocator.js';
|
||||||
import {ActionOptions, Locator} from './locators.js';
|
import {ActionOptions, Locator} from './locators.js';
|
||||||
@ -34,6 +34,13 @@ export type Predicate<From, To extends From = From> =
|
|||||||
| ((value: From) => value is To)
|
| ((value: From) => value is To)
|
||||||
| ((value: From) => Awaitable<boolean>);
|
| ((value: From) => Awaitable<boolean>);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export type HandlePredicate<From, To extends From = From> =
|
||||||
|
| ((value: HandleFor<From>, signal?: AbortSignal) => value is HandleFor<To>)
|
||||||
|
| ((value: HandleFor<From>, signal?: AbortSignal) => Awaitable<boolean>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
@ -41,9 +48,9 @@ export class FilteredLocator<From, To extends From> extends DelegatedLocator<
|
|||||||
From,
|
From,
|
||||||
To
|
To
|
||||||
> {
|
> {
|
||||||
#predicate: Predicate<From, To>;
|
#predicate: HandlePredicate<From, To>;
|
||||||
|
|
||||||
constructor(base: Locator<From>, predicate: Predicate<From, To>) {
|
constructor(base: Locator<From>, predicate: HandlePredicate<From, To>) {
|
||||||
super(base);
|
super(base);
|
||||||
this.#predicate = predicate;
|
this.#predicate = predicate;
|
||||||
}
|
}
|
||||||
@ -59,12 +66,11 @@ export class FilteredLocator<From, To extends From> extends DelegatedLocator<
|
|||||||
return this.delegate._wait(options).pipe(
|
return this.delegate._wait(options).pipe(
|
||||||
mergeMap(handle => {
|
mergeMap(handle => {
|
||||||
return from(
|
return from(
|
||||||
(handle as ElementHandle<Node>).frame.waitForFunction(
|
Promise.resolve(this.#predicate(handle, options?.signal))
|
||||||
this.#predicate,
|
|
||||||
{signal: options?.signal, timeout: this._timeout},
|
|
||||||
handle
|
|
||||||
)
|
|
||||||
).pipe(
|
).pipe(
|
||||||
|
filter(value => {
|
||||||
|
return value;
|
||||||
|
}),
|
||||||
map(() => {
|
map(() => {
|
||||||
// SAFETY: It passed the predicate, so this is correct.
|
// SAFETY: It passed the predicate, so this is correct.
|
||||||
return handle as HandleFor<To>;
|
return handle as HandleFor<To>;
|
||||||
|
@ -48,6 +48,7 @@ import {
|
|||||||
Action,
|
Action,
|
||||||
AwaitedLocator,
|
AwaitedLocator,
|
||||||
FilteredLocator,
|
FilteredLocator,
|
||||||
|
HandleMapper,
|
||||||
MappedLocator,
|
MappedLocator,
|
||||||
Mapper,
|
Mapper,
|
||||||
Predicate,
|
Predicate,
|
||||||
@ -702,7 +703,10 @@ export abstract class Locator<T> extends EventEmitter {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
map<To>(mapper: Mapper<T, To>): Locator<To> {
|
map<To>(mapper: Mapper<T, To>): Locator<To> {
|
||||||
return new MappedLocator(this._clone(), mapper);
|
return new MappedLocator(this._clone(), handle => {
|
||||||
|
// SAFETY: TypeScript cannot deduce the type.
|
||||||
|
return (handle as any).evaluateHandle(mapper);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -713,9 +717,38 @@ export abstract class Locator<T> extends EventEmitter {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
filter<S extends T>(predicate: Predicate<T, S>): Locator<S> {
|
filter<S extends T>(predicate: Predicate<T, S>): Locator<S> {
|
||||||
|
return new FilteredLocator(this._clone(), async (handle, signal) => {
|
||||||
|
await (handle as ElementHandle<Node>).frame.waitForFunction(
|
||||||
|
predicate,
|
||||||
|
{signal, timeout: this._timeout},
|
||||||
|
handle
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an expectation that is evaluated against located handles.
|
||||||
|
*
|
||||||
|
* If the expectations do not match, then the locator will retry.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
filterHandle<S extends T>(
|
||||||
|
predicate: Predicate<HandleFor<T>, HandleFor<S>>
|
||||||
|
): Locator<S> {
|
||||||
return new FilteredLocator(this._clone(), predicate);
|
return new FilteredLocator(this._clone(), predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps the locator using the provided mapper.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
mapHandle<To>(mapper: HandleMapper<T, To>): Locator<To> {
|
||||||
|
return new MappedLocator(this._clone(), mapper);
|
||||||
|
}
|
||||||
|
|
||||||
click<ElementType extends Element>(
|
click<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<LocatorClickOptions>
|
options?: Readonly<LocatorClickOptions>
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import {Observable, from, mergeMap} from '../../../third_party/rxjs/rxjs.js';
|
import {Observable, from, mergeMap} from '../../../third_party/rxjs/rxjs.js';
|
||||||
import {Awaitable, HandleFor} from '../../common/common.js';
|
import {Awaitable, HandleFor} from '../../common/common.js';
|
||||||
import {JSHandle} from '../JSHandle.js';
|
|
||||||
|
|
||||||
import {ActionOptions, DelegatedLocator, Locator} from './locators.js';
|
import {ActionOptions, DelegatedLocator, Locator} from './locators.js';
|
||||||
|
|
||||||
@ -28,10 +27,18 @@ export type Mapper<From, To> = (value: From) => Awaitable<To>;
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class MappedLocator<From, To> extends DelegatedLocator<From, To> {
|
export type HandleMapper<From, To> = (
|
||||||
#mapper: Mapper<From, To>;
|
value: HandleFor<From>,
|
||||||
|
signal?: AbortSignal
|
||||||
|
) => Awaitable<HandleFor<To>>;
|
||||||
|
|
||||||
constructor(base: Locator<From>, mapper: Mapper<From, To>) {
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class MappedLocator<From, To> extends DelegatedLocator<From, To> {
|
||||||
|
#mapper: HandleMapper<From, To>;
|
||||||
|
|
||||||
|
constructor(base: Locator<From>, mapper: HandleMapper<From, To>) {
|
||||||
super(base);
|
super(base);
|
||||||
this.#mapper = mapper;
|
this.#mapper = mapper;
|
||||||
}
|
}
|
||||||
@ -45,7 +52,7 @@ export class MappedLocator<From, To> extends DelegatedLocator<From, To> {
|
|||||||
override _wait(options?: Readonly<ActionOptions>): Observable<HandleFor<To>> {
|
override _wait(options?: Readonly<ActionOptions>): Observable<HandleFor<To>> {
|
||||||
return this.delegate._wait(options).pipe(
|
return this.delegate._wait(options).pipe(
|
||||||
mergeMap(handle => {
|
mergeMap(handle => {
|
||||||
return from((handle as JSHandle<From>).evaluateHandle(this.#mapper));
|
return from(Promise.resolve(this.#mapper(handle, options?.signal)));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user