mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
chore: use RxJS for locator implementation (#10607)
Using RxJS greatly simplifies the control flow for locators and comes with automatic cleanup on failure. It greatly simplifies the `signal` logic and the retry logic.
This commit is contained in:
parent
e8e7bf1d3c
commit
ede43ca2d3
@ -153,6 +153,7 @@ sidebar_label: API
|
|||||||
| [ActionResult](./puppeteer.actionresult.md) | |
|
| [ActionResult](./puppeteer.actionresult.md) | |
|
||||||
| [Awaitable](./puppeteer.awaitable.md) | |
|
| [Awaitable](./puppeteer.awaitable.md) | |
|
||||||
| [AwaitableIterable](./puppeteer.awaitableiterable.md) | |
|
| [AwaitableIterable](./puppeteer.awaitableiterable.md) | |
|
||||||
|
| [AwaitedLocator](./puppeteer.awaitedlocator.md) | |
|
||||||
| [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | |
|
| [ChromeReleaseChannel](./puppeteer.chromereleasechannel.md) | |
|
||||||
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
|
| [ConsoleMessageType](./puppeteer.consolemessagetype.md) | The supported types for console messages. |
|
||||||
| [ElementFor](./puppeteer.elementfor.md) | |
|
| [ElementFor](./puppeteer.elementfor.md) | |
|
||||||
@ -182,5 +183,4 @@ sidebar_label: API
|
|||||||
| [PuppeteerNodeLaunchOptions](./puppeteer.puppeteernodelaunchoptions.md) | Utility type exposed to enable users to define options that can be passed to <code>puppeteer.launch</code> without having to list the set of all types. |
|
| [PuppeteerNodeLaunchOptions](./puppeteer.puppeteernodelaunchoptions.md) | Utility type exposed to enable users to define options that can be passed to <code>puppeteer.launch</code> without having to list the set of all types. |
|
||||||
| [ResourceType](./puppeteer.resourcetype.md) | Resource types for HTTPRequests as perceived by the rendering engine. |
|
| [ResourceType](./puppeteer.resourcetype.md) | Resource types for HTTPRequests as perceived by the rendering engine. |
|
||||||
| [TargetFilterCallback](./puppeteer.targetfiltercallback.md) | |
|
| [TargetFilterCallback](./puppeteer.targetfiltercallback.md) | |
|
||||||
| [UnionLocatorOf](./puppeteer.unionlocatorof.md) | |
|
|
||||||
| [VisibilityOption](./puppeteer.visibilityoption.md) | |
|
| [VisibilityOption](./puppeteer.visibilityoption.md) | |
|
||||||
|
13
docs/api/puppeteer.awaitedlocator.md
Normal file
13
docs/api/puppeteer.awaitedlocator.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: AwaitedLocator
|
||||||
|
---
|
||||||
|
|
||||||
|
# AwaitedLocator type
|
||||||
|
|
||||||
|
#### Signature:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
export type AwaitedLocator<T> = T extends Locator<infer S> ? S : never;
|
||||||
|
```
|
||||||
|
|
||||||
|
**References:** [Locator](./puppeteer.locator.md)
|
@ -8,7 +8,7 @@ sidebar_label: Locator.click
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract click<ElementType extends Element>(
|
click<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<LocatorClickOptions>
|
options?: Readonly<LocatorClickOptions>
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
@ -10,7 +10,7 @@ Fills out the input identified by the locator using the provided value. The type
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract fill<ElementType extends Element>(
|
fill<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
value: string,
|
value: string,
|
||||||
options?: Readonly<ActionOptions>
|
options?: Readonly<ActionOptions>
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.hover
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract hover<ElementType extends Element>(
|
hover<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<ActionOptions>
|
options?: Readonly<ActionOptions>
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
@ -22,18 +22,18 @@ export declare abstract class Locator<T> extends EventEmitter
|
|||||||
|
|
||||||
## Methods
|
## Methods
|
||||||
|
|
||||||
| Method | Modifiers | Description |
|
| Method | Modifiers | Description |
|
||||||
| ------------------------------------------------------------------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------------------------------------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [click(this, options)](./puppeteer.locator.click.md) | | |
|
| [click(this, options)](./puppeteer.locator.click.md) | | |
|
||||||
| [fill(this, value, options)](./puppeteer.locator.fill.md) | | Fills out the input identified by the locator using the provided value. The type of the input is determined at runtime and the appropriate fill-out method is chosen based on the type. contenteditable, selector, inputs are supported. |
|
| [fill(this, value, options)](./puppeteer.locator.fill.md) | | Fills out the input identified by the locator using the provided value. The type of the input is determined at runtime and the appropriate fill-out method is chosen based on the type. contenteditable, selector, inputs are supported. |
|
||||||
| [hover(this, options)](./puppeteer.locator.hover.md) | | |
|
| [hover(this, options)](./puppeteer.locator.hover.md) | | |
|
||||||
| [off(eventName, handler)](./puppeteer.locator.off.md) | | |
|
| [off(eventName, handler)](./puppeteer.locator.off.md) | | |
|
||||||
| [on(eventName, handler)](./puppeteer.locator.on.md) | | |
|
| [on(eventName, handler)](./puppeteer.locator.on.md) | | |
|
||||||
| [once(eventName, handler)](./puppeteer.locator.once.md) | | |
|
| [once(eventName, handler)](./puppeteer.locator.once.md) | | |
|
||||||
| [race(locators)](./puppeteer.locator.race.md) | <code>static</code> | Creates a race between multiple locators but ensures that only a single one acts. |
|
| [race(locators)](./puppeteer.locator.race.md) | <code>static</code> | Creates a race between multiple locators but ensures that only a single one acts. |
|
||||||
| [scroll(this, options)](./puppeteer.locator.scroll.md) | | |
|
| [scroll(this, options)](./puppeteer.locator.scroll.md) | | |
|
||||||
| [setEnsureElementIsInTheViewport(value)](./puppeteer.locator.setensureelementisintheviewport.md) | | |
|
| [setEnsureElementIsInTheViewport(this, value)](./puppeteer.locator.setensureelementisintheviewport.md) | | |
|
||||||
| [setTimeout(timeout)](./puppeteer.locator.settimeout.md) | | |
|
| [setTimeout(timeout)](./puppeteer.locator.settimeout.md) | | |
|
||||||
| [setVisibility(visibility)](./puppeteer.locator.setvisibility.md) | | |
|
| [setVisibility(this, visibility)](./puppeteer.locator.setvisibility.md) | | |
|
||||||
| [setWaitForEnabled(value)](./puppeteer.locator.setwaitforenabled.md) | | |
|
| [setWaitForEnabled(this, value)](./puppeteer.locator.setwaitforenabled.md) | | |
|
||||||
| [setWaitForStableBoundingBox(value)](./puppeteer.locator.setwaitforstableboundingbox.md) | | |
|
| [setWaitForStableBoundingBox(this, value)](./puppeteer.locator.setwaitforstableboundingbox.md) | | |
|
||||||
|
@ -10,9 +10,9 @@ Creates a race between multiple locators but ensures that only a single one acts
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
static race<Locators extends Array<Locator<unknown>>>(
|
static race<Locators extends readonly unknown[] | []>(
|
||||||
locators: Locators
|
locators: Locators
|
||||||
): Locator<UnionLocatorOf<Locators>>;
|
): Locator<AwaitedLocator<Locators[number]>>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -24,4 +24,4 @@ class Locator {
|
|||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
[Locator](./puppeteer.locator.md)<[UnionLocatorOf](./puppeteer.unionlocatorof.md)<Locators>>
|
[Locator](./puppeteer.locator.md)<[AwaitedLocator](./puppeteer.awaitedlocator.md)<Locators\[number\]>>
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.scroll
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract scroll<ElementType extends Element>(
|
scroll<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<LocatorScrollOptions>
|
options?: Readonly<LocatorScrollOptions>
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
|
@ -8,16 +8,20 @@ sidebar_label: Locator.setEnsureElementIsInTheViewport
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract setEnsureElementIsInTheViewport(value: boolean): this;
|
setEnsureElementIsInTheViewport<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ElementType>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------- | ----------- |
|
| --------- | ---------------------------------------------------- | ----------- |
|
||||||
| value | boolean | |
|
| this | [Locator](./puppeteer.locator.md)<ElementType> | |
|
||||||
|
| value | boolean | |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
this
|
[Locator](./puppeteer.locator.md)<ElementType>
|
||||||
|
@ -8,7 +8,7 @@ sidebar_label: Locator.setTimeout
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract setTimeout(timeout: number): this;
|
setTimeout(timeout: number): this;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -8,7 +8,10 @@ sidebar_label: Locator.setVisibility
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract setVisibility(visibility: VisibilityOption): this;
|
setVisibility<NodeType extends Node>(
|
||||||
|
this: Locator<NodeType>,
|
||||||
|
visibility: VisibilityOption
|
||||||
|
): Locator<NodeType>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -16,8 +19,9 @@ class Locator {
|
|||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| ---------- | --------------------------------------------------- | ----------- |
|
| ---------- | --------------------------------------------------- | ----------- |
|
||||||
|
| this | [Locator](./puppeteer.locator.md)<NodeType> | |
|
||||||
| visibility | [VisibilityOption](./puppeteer.visibilityoption.md) | |
|
| visibility | [VisibilityOption](./puppeteer.visibilityoption.md) | |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
this
|
[Locator](./puppeteer.locator.md)<NodeType>
|
||||||
|
@ -8,16 +8,20 @@ sidebar_label: Locator.setWaitForEnabled
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract setWaitForEnabled(value: boolean): this;
|
setWaitForEnabled<NodeType extends Node>(
|
||||||
|
this: Locator<NodeType>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<NodeType>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------- | ----------- |
|
| --------- | ------------------------------------------------- | ----------- |
|
||||||
| value | boolean | |
|
| this | [Locator](./puppeteer.locator.md)<NodeType> | |
|
||||||
|
| value | boolean | |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
this
|
[Locator](./puppeteer.locator.md)<NodeType>
|
||||||
|
@ -8,16 +8,20 @@ sidebar_label: Locator.setWaitForStableBoundingBox
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
class Locator {
|
class Locator {
|
||||||
abstract setWaitForStableBoundingBox(value: boolean): this;
|
setWaitForStableBoundingBox<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ElementType>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------- | ----------- |
|
| --------- | ---------------------------------------------------- | ----------- |
|
||||||
| value | boolean | |
|
| this | [Locator](./puppeteer.locator.md)<ElementType> | |
|
||||||
|
| value | boolean | |
|
||||||
|
|
||||||
**Returns:**
|
**Returns:**
|
||||||
|
|
||||||
this
|
[Locator](./puppeteer.locator.md)<ElementType>
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
sidebar_label: UnionLocatorOf
|
|
||||||
---
|
|
||||||
|
|
||||||
# UnionLocatorOf type
|
|
||||||
|
|
||||||
#### Signature:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export type UnionLocatorOf<T> = T extends Array<Locator<infer S>> ? S : never;
|
|
||||||
```
|
|
||||||
|
|
||||||
**References:** [Locator](./puppeteer.locator.md)
|
|
409
package-lock.json
generated
409
package-lock.json
generated
@ -18,8 +18,8 @@
|
|||||||
"@microsoft/api-extractor-model": "7.27.4",
|
"@microsoft/api-extractor-model": "7.27.4",
|
||||||
"@pptr/testserver": "file:packages/testserver",
|
"@pptr/testserver": "file:packages/testserver",
|
||||||
"@prettier/sync": "0.2.1",
|
"@prettier/sync": "0.2.1",
|
||||||
"@rollup/plugin-commonjs": "25.0.2",
|
|
||||||
"@rollup/plugin-node-resolve": "15.1.0",
|
"@rollup/plugin-node-resolve": "15.1.0",
|
||||||
|
"@rollup/plugin-terser": "0.4.3",
|
||||||
"@types/debug": "4.1.8",
|
"@types/debug": "4.1.8",
|
||||||
"@types/diff": "5.0.3",
|
"@types/diff": "5.0.3",
|
||||||
"@types/mime": "3.0.1",
|
"@types/mime": "3.0.1",
|
||||||
@ -1528,6 +1528,20 @@
|
|||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/resolve-uri": {
|
"node_modules/@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -1536,10 +1550,39 @@
|
|||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/set-array": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/source-map": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
|
||||||
|
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.0",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14",
|
"version": "1.4.14",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||||
|
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/resolve-uri": "3.1.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@microsoft/api-documenter": {
|
"node_modules/@microsoft/api-documenter": {
|
||||||
"version": "7.22.27",
|
"version": "7.22.27",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.22.27.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.22.27.tgz",
|
||||||
@ -1935,76 +1978,6 @@
|
|||||||
"resolved": "packages/ng-schematics",
|
"resolved": "packages/ng-schematics",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/plugin-commonjs": {
|
|
||||||
"version": "25.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.2.tgz",
|
|
||||||
"integrity": "sha512-NGTwaJxIO0klMs+WSFFtBP7b9TdTJ3K76HZkewT8/+yHzMiUGVQgaPtLQxNVYIgT5F7lxkEyVID+yS3K7bhCow==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@rollup/pluginutils": "^5.0.1",
|
|
||||||
"commondir": "^1.0.1",
|
|
||||||
"estree-walker": "^2.0.2",
|
|
||||||
"glob": "^8.0.3",
|
|
||||||
"is-reference": "1.2.1",
|
|
||||||
"magic-string": "^0.27.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": "^2.68.0||^3.0.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"rollup": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-commonjs/node_modules/glob": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^5.0.1",
|
|
||||||
"once": "^1.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-commonjs/node_modules/minimatch": {
|
|
||||||
"version": "5.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
|
||||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-node-resolve": {
|
"node_modules/@rollup/plugin-node-resolve": {
|
||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz",
|
||||||
@ -2035,6 +2008,37 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/plugin-terser": {
|
||||||
|
"version": "0.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.3.tgz",
|
||||||
|
"integrity": "sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"serialize-javascript": "^6.0.1",
|
||||||
|
"smob": "^1.0.0",
|
||||||
|
"terser": "^5.17.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.x || ^3.x"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -3529,11 +3533,6 @@
|
|||||||
"node": "^12.20.0 || >=14"
|
"node": "^12.20.0 || >=14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commondir": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/commonmark": {
|
"node_modules/commonmark": {
|
||||||
"version": "0.30.0",
|
"version": "0.30.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -6293,14 +6292,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/is-reference": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-regex": {
|
"node_modules/is-regex": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -6885,17 +6876,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/magic-string": {
|
|
||||||
"version": "0.27.0",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/make-dir": {
|
"node_modules/make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -9344,6 +9324,12 @@
|
|||||||
"npm": ">= 3.0.0"
|
"npm": ">= 3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/smob": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/smob/-/smob-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-MqR3fVulhjWuRNSMydnTlweu38UhQ0HXM4buStD/S3mc/BzX3CuM9OmhyQpmtYCvoYdl5ris6TI0ZqH355Ymqg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/socks": {
|
"node_modules/socks": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -9761,6 +9747,30 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/terser": {
|
||||||
|
"version": "5.19.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
|
||||||
|
"integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
"acorn": "^8.8.2",
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map-support": "~0.5.20"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"terser": "bin/terser"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/terser/node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/test-exclude": {
|
"node_modules/test-exclude": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -10148,15 +10158,6 @@
|
|||||||
"node": ">=10.12.0"
|
"node": ">=10.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": {
|
|
||||||
"version": "0.3.16",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/resolve-uri": "3.1.0",
|
|
||||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/validate-npm-package-license": {
|
"node_modules/validate-npm-package-license": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -10570,12 +10571,28 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mitt": "3.0.0",
|
"mitt": "3.0.0",
|
||||||
"parsel-js": "1.1.0"
|
"parsel-js": "1.1.0",
|
||||||
|
"rxjs": "7.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.3.0"
|
"node": ">=16.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/puppeteer-core/node_modules/rxjs": {
|
||||||
|
"version": "7.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||||
|
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages/puppeteer-core/node_modules/tslib": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"packages/testserver": {
|
"packages/testserver": {
|
||||||
"name": "@pptr/testserver",
|
"name": "@pptr/testserver",
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
@ -11463,13 +11480,50 @@
|
|||||||
"chalk": "^4.0.0"
|
"chalk": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@jridgewell/gen-mapping": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@jridgewell/resolve-uri": {
|
"@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@jridgewell/set-array": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@jridgewell/source-map": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz",
|
||||||
|
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.0",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.9"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@jridgewell/sourcemap-codec": {
|
"@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14"
|
"version": "1.4.14"
|
||||||
},
|
},
|
||||||
|
"@jridgewell/trace-mapping": {
|
||||||
|
"version": "0.3.18",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||||
|
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@jridgewell/resolve-uri": "3.1.0",
|
||||||
|
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@microsoft/api-documenter": {
|
"@microsoft/api-documenter": {
|
||||||
"version": "7.22.27",
|
"version": "7.22.27",
|
||||||
"resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.22.27.tgz",
|
"resolved": "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.22.27.tgz",
|
||||||
@ -11823,57 +11877,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@rollup/plugin-commonjs": {
|
|
||||||
"version": "25.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.2.tgz",
|
|
||||||
"integrity": "sha512-NGTwaJxIO0klMs+WSFFtBP7b9TdTJ3K76HZkewT8/+yHzMiUGVQgaPtLQxNVYIgT5F7lxkEyVID+yS3K7bhCow==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@rollup/pluginutils": "^5.0.1",
|
|
||||||
"commondir": "^1.0.1",
|
|
||||||
"estree-walker": "^2.0.2",
|
|
||||||
"glob": "^8.0.3",
|
|
||||||
"is-reference": "1.2.1",
|
|
||||||
"magic-string": "^0.27.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"balanced-match": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"estree-walker": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"glob": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"fs.realpath": "^1.0.0",
|
|
||||||
"inflight": "^1.0.4",
|
|
||||||
"inherits": "2",
|
|
||||||
"minimatch": "^5.0.1",
|
|
||||||
"once": "^1.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minimatch": {
|
|
||||||
"version": "5.1.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
|
||||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"brace-expansion": "^2.0.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@rollup/plugin-node-resolve": {
|
"@rollup/plugin-node-resolve": {
|
||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.1.0.tgz",
|
||||||
@ -11894,6 +11897,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@rollup/plugin-terser": {
|
||||||
|
"version": "0.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.3.tgz",
|
||||||
|
"integrity": "sha512-EF0oejTMtkyhrkwCdg0HJ0IpkcaVg1MMSf2olHb2Jp+1mnLM04OhjpJWGma4HobiDTF0WCyViWuvadyE9ch2XA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"serialize-javascript": "^6.0.1",
|
||||||
|
"smob": "^1.0.0",
|
||||||
|
"terser": "^5.17.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"serialize-javascript": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/pluginutils": {
|
"@rollup/pluginutils": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -12938,10 +12963,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"commondir": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"commonmark": {
|
"commonmark": {
|
||||||
"version": "0.30.0",
|
"version": "0.30.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -14726,13 +14747,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"is-reference": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@types/estree": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is-regex": {
|
"is-regex": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -15152,13 +15166,6 @@
|
|||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"magic-string": {
|
|
||||||
"version": "0.27.0",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -16337,7 +16344,25 @@
|
|||||||
"devtools-protocol": "0.0.1147663",
|
"devtools-protocol": "0.0.1147663",
|
||||||
"mitt": "3.0.0",
|
"mitt": "3.0.0",
|
||||||
"parsel-js": "1.1.0",
|
"parsel-js": "1.1.0",
|
||||||
|
"rxjs": "7.8.1",
|
||||||
"ws": "8.13.0"
|
"ws": "8.13.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"rxjs": {
|
||||||
|
"version": "7.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||||
|
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"queue-microtask": {
|
"queue-microtask": {
|
||||||
@ -16845,6 +16870,12 @@
|
|||||||
"smart-buffer": {
|
"smart-buffer": {
|
||||||
"version": "4.2.0"
|
"version": "4.2.0"
|
||||||
},
|
},
|
||||||
|
"smob": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/smob/-/smob-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-MqR3fVulhjWuRNSMydnTlweu38UhQ0HXM4buStD/S3mc/BzX3CuM9OmhyQpmtYCvoYdl5ris6TI0ZqH355Ymqg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"socks": {
|
"socks": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -17155,6 +17186,26 @@
|
|||||||
"streamx": "^2.15.0"
|
"streamx": "^2.15.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"terser": {
|
||||||
|
"version": "5.19.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
|
||||||
|
"integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
"acorn": "^8.8.2",
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map-support": "~0.5.20"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"test-exclude": {
|
"test-exclude": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -17420,16 +17471,6 @@
|
|||||||
"@jridgewell/trace-mapping": "^0.3.12",
|
"@jridgewell/trace-mapping": "^0.3.12",
|
||||||
"@types/istanbul-lib-coverage": "^2.0.1",
|
"@types/istanbul-lib-coverage": "^2.0.1",
|
||||||
"convert-source-map": "^1.6.0"
|
"convert-source-map": "^1.6.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/trace-mapping": {
|
|
||||||
"version": "0.3.16",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@jridgewell/resolve-uri": "3.1.0",
|
|
||||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validate-npm-package-license": {
|
"validate-npm-package-license": {
|
||||||
|
@ -110,8 +110,8 @@
|
|||||||
"@microsoft/api-extractor-model": "7.27.4",
|
"@microsoft/api-extractor-model": "7.27.4",
|
||||||
"@pptr/testserver": "file:packages/testserver",
|
"@pptr/testserver": "file:packages/testserver",
|
||||||
"@prettier/sync": "0.2.1",
|
"@prettier/sync": "0.2.1",
|
||||||
"@rollup/plugin-commonjs": "25.0.2",
|
|
||||||
"@rollup/plugin-node-resolve": "15.1.0",
|
"@rollup/plugin-node-resolve": "15.1.0",
|
||||||
|
"@rollup/plugin-terser": "0.4.3",
|
||||||
"@types/debug": "4.1.8",
|
"@types/debug": "4.1.8",
|
||||||
"@types/diff": "5.0.3",
|
"@types/diff": "5.0.3",
|
||||||
"@types/mime": "3.0.1",
|
"@types/mime": "3.0.1",
|
||||||
|
@ -152,6 +152,7 @@
|
|||||||
"@puppeteer/browsers": "1.4.6"
|
"@puppeteer/browsers": "1.4.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"rxjs": "7.8.1",
|
||||||
"mitt": "3.0.0",
|
"mitt": "3.0.0",
|
||||||
"parsel-js": "1.1.0"
|
"parsel-js": "1.1.0"
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
|
||||||
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
||||||
|
import terser from '@rollup/plugin-terser';
|
||||||
import {globSync} from 'glob';
|
import {globSync} from 'glob';
|
||||||
|
|
||||||
export default ['cjs', 'esm'].flatMap(outputType => {
|
export default ['cjs', 'esm'].flatMap(outputType => {
|
||||||
@ -28,7 +28,7 @@ export default ['cjs', 'esm'].flatMap(outputType => {
|
|||||||
file,
|
file,
|
||||||
format: outputType,
|
format: outputType,
|
||||||
},
|
},
|
||||||
plugins: [commonjs(), nodeResolve()],
|
plugins: [terser(), nodeResolve()],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return configs;
|
return configs;
|
||||||
|
@ -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, UnionLocatorOf} from './locators/locators.js';
|
import {AwaitedLocator, Locator, NodeLocator} from './locators/locators.js';
|
||||||
import type {Target} from './Target.js';
|
import type {Target} from './Target.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -850,10 +850,10 @@ export class Page extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
locatorRace<Locators extends Array<Locator<unknown>>>(
|
locatorRace<Locators extends readonly unknown[] | []>(
|
||||||
locators: Locators
|
locators: Locators
|
||||||
): Locator<UnionLocatorOf<Locators>> {
|
): Locator<AwaitedLocator<Locators[number]>> {
|
||||||
return Locator.race(locators as Array<Locator<UnionLocatorOf<Locators>>>);
|
return Locator.race(locators);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,16 +1,30 @@
|
|||||||
import {Awaitable} from '../../common/common.js';
|
/**
|
||||||
import {ElementHandle} from '../ElementHandle.js';
|
* 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 {
|
import {
|
||||||
ActionOptions,
|
Observable,
|
||||||
LOCATOR_CONTEXTS,
|
from,
|
||||||
Locator,
|
map,
|
||||||
LocatorClickOptions,
|
mergeMap,
|
||||||
LocatorContext,
|
throwIfEmpty,
|
||||||
LocatorScrollOptions,
|
} from '../../../third_party/rxjs/rxjs.js';
|
||||||
VisibilityOption,
|
import {Awaitable, HandleFor} from '../../common/common.js';
|
||||||
type ActionCondition,
|
import {ElementHandle} from '../ElementHandle.js';
|
||||||
} from './locators.js';
|
|
||||||
|
import {ActionOptions, Locator, VisibilityOption} from './locators.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -31,76 +45,75 @@ export class ExpectedLocator<From, To extends From> extends Locator<To> {
|
|||||||
|
|
||||||
this.#base = base;
|
this.#base = base;
|
||||||
this.#predicate = predicate;
|
this.#predicate = predicate;
|
||||||
|
|
||||||
|
this.copyOptions(this.#base);
|
||||||
}
|
}
|
||||||
|
|
||||||
override setVisibility(visibility: VisibilityOption): this {
|
|
||||||
this.#base.setVisibility(visibility);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
override setTimeout(timeout: number): this {
|
override setTimeout(timeout: number): this {
|
||||||
|
super.setTimeout(timeout);
|
||||||
this.#base.setTimeout(timeout);
|
this.#base.setTimeout(timeout);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
override setEnsureElementIsInTheViewport(value: boolean): this {
|
|
||||||
this.#base.setEnsureElementIsInTheViewport(value);
|
override setVisibility<FromNode extends Node, ToNode extends FromNode>(
|
||||||
|
this: ExpectedLocator<FromNode, ToNode>,
|
||||||
|
visibility: VisibilityOption
|
||||||
|
): Locator<ToNode> {
|
||||||
|
super.setVisibility(visibility);
|
||||||
|
this.#base.setVisibility(visibility);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
override setWaitForEnabled(value: boolean): this {
|
|
||||||
|
override setWaitForEnabled<FromNode extends Node, ToNode extends FromNode>(
|
||||||
|
this: ExpectedLocator<FromNode, ToNode>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ToNode> {
|
||||||
|
super.setWaitForEnabled(value);
|
||||||
this.#base.setWaitForEnabled(value);
|
this.#base.setWaitForEnabled(value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
override setWaitForStableBoundingBox(value: boolean): this {
|
|
||||||
|
override setEnsureElementIsInTheViewport<
|
||||||
|
FromElement extends Element,
|
||||||
|
ToElement extends FromElement,
|
||||||
|
>(
|
||||||
|
this: ExpectedLocator<FromElement, ToElement>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ToElement> {
|
||||||
|
super.setEnsureElementIsInTheViewport(value);
|
||||||
|
this.#base.setEnsureElementIsInTheViewport(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override setWaitForStableBoundingBox<
|
||||||
|
FromElement extends Element,
|
||||||
|
ToElement extends FromElement,
|
||||||
|
>(
|
||||||
|
this: ExpectedLocator<FromElement, ToElement>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ToElement> {
|
||||||
|
super.setWaitForStableBoundingBox(value);
|
||||||
this.#base.setWaitForStableBoundingBox(value);
|
this.#base.setWaitForStableBoundingBox(value);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
#condition: ActionCondition<From> = async (handle, signal) => {
|
override _wait(options?: Readonly<ActionOptions>): Observable<HandleFor<To>> {
|
||||||
// TODO(jrandolf): We should remove this once JSHandle has waitForFunction.
|
return this.#base._wait(options).pipe(
|
||||||
await (handle as ElementHandle<Node>).frame.waitForFunction(
|
mergeMap(handle => {
|
||||||
this.#predicate,
|
return from(
|
||||||
{signal},
|
(handle as ElementHandle<Node>).frame.waitForFunction(
|
||||||
handle
|
this.#predicate,
|
||||||
|
{signal: options?.signal, timeout: this.timeout},
|
||||||
|
handle
|
||||||
|
)
|
||||||
|
).pipe(
|
||||||
|
map(() => {
|
||||||
|
// SAFETY: It passed the predicate, so this is correct.
|
||||||
|
return handle as HandleFor<To>;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
throwIfEmpty()
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
#insertFilterCondition<
|
|
||||||
FromElement extends Node,
|
|
||||||
ToElement extends FromElement,
|
|
||||||
>(this: ExpectedLocator<FromElement, ToElement>): void {
|
|
||||||
const context = (LOCATOR_CONTEXTS.get(this.#base) ??
|
|
||||||
{}) as LocatorContext<FromElement>;
|
|
||||||
context.conditions ??= new Set();
|
|
||||||
context.conditions.add(this.#condition);
|
|
||||||
LOCATOR_CONTEXTS.set(this.#base, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
override click<FromElement extends Element, ToElement extends FromElement>(
|
|
||||||
this: ExpectedLocator<FromElement, ToElement>,
|
|
||||||
options?: Readonly<LocatorClickOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
this.#insertFilterCondition();
|
|
||||||
return this.#base.click(options);
|
|
||||||
}
|
|
||||||
override fill<FromElement extends Element, ToElement extends FromElement>(
|
|
||||||
this: ExpectedLocator<FromElement, ToElement>,
|
|
||||||
value: string,
|
|
||||||
options?: Readonly<ActionOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
this.#insertFilterCondition();
|
|
||||||
return this.#base.fill(value, options);
|
|
||||||
}
|
|
||||||
override hover<FromElement extends Element, ToElement extends FromElement>(
|
|
||||||
this: ExpectedLocator<FromElement, ToElement>,
|
|
||||||
options?: Readonly<ActionOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
this.#insertFilterCondition();
|
|
||||||
return this.#base.hover(options);
|
|
||||||
}
|
|
||||||
override scroll<FromElement extends Element, ToElement extends FromElement>(
|
|
||||||
this: ExpectedLocator<FromElement, ToElement>,
|
|
||||||
options?: Readonly<LocatorScrollOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
this.#insertFilterCondition();
|
|
||||||
return this.#base.scroll(options);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,67 @@
|
|||||||
import {EventEmitter} from '../../common/EventEmitter.js';
|
/**
|
||||||
import {ClickOptions} from '../ElementHandle.js';
|
* 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 {
|
import {
|
||||||
|
EMPTY,
|
||||||
|
Observable,
|
||||||
|
OperatorFunction,
|
||||||
|
catchError,
|
||||||
|
defaultIfEmpty,
|
||||||
|
defer,
|
||||||
|
filter,
|
||||||
|
first,
|
||||||
|
firstValueFrom,
|
||||||
|
from,
|
||||||
|
fromEvent,
|
||||||
|
identity,
|
||||||
|
ignoreElements,
|
||||||
|
map,
|
||||||
|
merge,
|
||||||
|
mergeMap,
|
||||||
|
noop,
|
||||||
|
pipe,
|
||||||
|
raceWith,
|
||||||
|
retry,
|
||||||
|
tap,
|
||||||
|
timer,
|
||||||
|
} from '../../../third_party/rxjs/rxjs.js';
|
||||||
|
import {TimeoutError} from '../../common/Errors.js';
|
||||||
|
import {EventEmitter} from '../../common/EventEmitter.js';
|
||||||
|
import {HandleFor} from '../../common/types.js';
|
||||||
|
import {debugError} from '../../common/util.js';
|
||||||
|
import {BoundingBox, ClickOptions, ElementHandle} from '../ElementHandle.js';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Action,
|
||||||
|
AwaitedLocator,
|
||||||
ExpectedLocator,
|
ExpectedLocator,
|
||||||
Predicate,
|
Predicate,
|
||||||
RaceLocator,
|
RaceLocator,
|
||||||
UnionLocatorOf,
|
|
||||||
type ActionCondition,
|
|
||||||
} from './locators.js';
|
} from './locators.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* For observables coming from promises, a delay is needed, otherwise RxJS will
|
||||||
|
* never yield in a permanent failure for a promise.
|
||||||
|
*
|
||||||
|
* We also don't want RxJS to do promise operations to often, so we bump the
|
||||||
|
* delay up to 100ms.
|
||||||
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export interface LocatorContext<T> {
|
export const RETRY_DELAY = 100;
|
||||||
conditions?: Set<ActionCondition<T>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export const LOCATOR_CONTEXTS = new WeakMap<
|
|
||||||
Locator<unknown>,
|
|
||||||
LocatorContext<never>
|
|
||||||
>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -113,33 +152,81 @@ export interface LocatorEventObject {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export abstract class Locator<T> extends EventEmitter {
|
export abstract class Locator<T> extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* Creates a race between multiple locators but ensures that only a single one
|
||||||
|
* acts.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static race<Locators extends readonly unknown[] | []>(
|
||||||
|
locators: Locators
|
||||||
|
): Locator<AwaitedLocator<Locators[number]>> {
|
||||||
|
return RaceLocator.create(locators);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for nominally typing {@link Locator}.
|
* Used for nominally typing {@link Locator}.
|
||||||
*/
|
*/
|
||||||
declare _?: T;
|
declare _?: T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a race between multiple locators but ensures that only a single one
|
|
||||||
* acts.
|
|
||||||
*/
|
|
||||||
static race<Locators extends Array<Locator<unknown>>>(
|
|
||||||
locators: Locators
|
|
||||||
): Locator<UnionLocatorOf<Locators>> {
|
|
||||||
return new RaceLocator(
|
|
||||||
locators as Array<Locator<UnionLocatorOf<Locators>>>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an expectation that is evaluated against located values.
|
|
||||||
*
|
|
||||||
* If the expectations do not match, then the locator will retry.
|
|
||||||
*
|
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
expect<S extends T>(predicate: Predicate<T, S>): Locator<S> {
|
protected visibility: VisibilityOption = null;
|
||||||
return new ExpectedLocator(this, predicate);
|
/**
|
||||||
}
|
* @internal
|
||||||
|
*/
|
||||||
|
protected timeout = 30_000;
|
||||||
|
#ensureElementIsInTheViewport = true;
|
||||||
|
#waitForEnabled = true;
|
||||||
|
#waitForStableBoundingBox = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
protected operators = {
|
||||||
|
conditions: (
|
||||||
|
conditions: Array<Action<T, never>>,
|
||||||
|
signal?: AbortSignal
|
||||||
|
): OperatorFunction<HandleFor<T>, HandleFor<T>> => {
|
||||||
|
return mergeMap((handle: HandleFor<T>) => {
|
||||||
|
return merge(
|
||||||
|
...conditions.map(condition => {
|
||||||
|
return condition(handle, signal);
|
||||||
|
})
|
||||||
|
).pipe(defaultIfEmpty(handle));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
retryAndRaceWithSignalAndTimer: <T>(
|
||||||
|
signal?: AbortSignal
|
||||||
|
): OperatorFunction<T, T> => {
|
||||||
|
const candidates = [];
|
||||||
|
if (signal) {
|
||||||
|
candidates.push(
|
||||||
|
fromEvent(signal, 'abort').pipe(
|
||||||
|
map(() => {
|
||||||
|
throw signal.reason;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.timeout > 0) {
|
||||||
|
candidates.push(
|
||||||
|
timer(this.timeout).pipe(
|
||||||
|
map(() => {
|
||||||
|
throw new TimeoutError(
|
||||||
|
`Timed out after waiting ${this.timeout}ms`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return pipe(
|
||||||
|
retry({delay: RETRY_DELAY}),
|
||||||
|
raceWith<T, never[]>(...candidates)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
override on<K extends keyof LocatorEventObject>(
|
override on<K extends keyof LocatorEventObject>(
|
||||||
eventName: K,
|
eventName: K,
|
||||||
@ -162,20 +249,409 @@ export abstract class Locator<T> extends EventEmitter {
|
|||||||
return super.off(eventName, handler);
|
return super.off(eventName, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract setVisibility(visibility: VisibilityOption): this;
|
setTimeout(timeout: number): this {
|
||||||
|
this.timeout = timeout;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
abstract setTimeout(timeout: number): this;
|
setVisibility<NodeType extends Node>(
|
||||||
|
this: Locator<NodeType>,
|
||||||
|
visibility: VisibilityOption
|
||||||
|
): Locator<NodeType> {
|
||||||
|
this.visibility = visibility;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
abstract setEnsureElementIsInTheViewport(value: boolean): this;
|
setWaitForEnabled<NodeType extends Node>(
|
||||||
|
this: Locator<NodeType>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<NodeType> {
|
||||||
|
this.#waitForEnabled = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
abstract setWaitForEnabled(value: boolean): this;
|
setEnsureElementIsInTheViewport<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ElementType> {
|
||||||
|
this.#ensureElementIsInTheViewport = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
abstract setWaitForStableBoundingBox(value: boolean): this;
|
setWaitForStableBoundingBox<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
value: boolean
|
||||||
|
): Locator<ElementType> {
|
||||||
|
this.#waitForStableBoundingBox = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
abstract click<ElementType extends Element>(
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
copyOptions(locator: Locator<any>): this {
|
||||||
|
this.timeout = locator.timeout;
|
||||||
|
this.visibility = locator.visibility;
|
||||||
|
this.#waitForEnabled = locator.#waitForEnabled;
|
||||||
|
this.#ensureElementIsInTheViewport = locator.#ensureElementIsInTheViewport;
|
||||||
|
this.#waitForStableBoundingBox = locator.#waitForStableBoundingBox;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the element has a "disabled" property, wait for the element to be
|
||||||
|
* enabled.
|
||||||
|
*/
|
||||||
|
#waitForEnabledIfNeeded = <ElementType extends Node>(
|
||||||
|
handle: HandleFor<ElementType>,
|
||||||
|
signal?: AbortSignal
|
||||||
|
): Observable<never> => {
|
||||||
|
if (!this.#waitForEnabled) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
return from(
|
||||||
|
handle.frame.page().waitForFunction(
|
||||||
|
element => {
|
||||||
|
if ('disabled' in element && typeof element.disabled === 'boolean') {
|
||||||
|
return !element.disabled;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: this.timeout,
|
||||||
|
signal,
|
||||||
|
},
|
||||||
|
handle
|
||||||
|
)
|
||||||
|
).pipe(ignoreElements());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the bounding box of the element for two consecutive animation
|
||||||
|
* frames and waits till they are the same.
|
||||||
|
*/
|
||||||
|
#waitForStableBoundingBoxIfNeeded = <ElementType extends Element>(
|
||||||
|
handle: HandleFor<ElementType>
|
||||||
|
): Observable<never> => {
|
||||||
|
if (!this.#waitForStableBoundingBox) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
return defer(() => {
|
||||||
|
// Note we don't use waitForFunction because that relies on RAF.
|
||||||
|
return from(
|
||||||
|
handle.evaluate(element => {
|
||||||
|
return new Promise<[BoundingBox, BoundingBox]>(resolve => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
const rect1 = element.getBoundingClientRect();
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
const rect2 = element.getBoundingClientRect();
|
||||||
|
resolve([
|
||||||
|
{
|
||||||
|
x: rect1.x,
|
||||||
|
y: rect1.y,
|
||||||
|
width: rect1.width,
|
||||||
|
height: rect1.height,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: rect2.x,
|
||||||
|
y: rect2.y,
|
||||||
|
width: rect2.width,
|
||||||
|
height: rect2.height,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}).pipe(
|
||||||
|
first(([rect1, rect2]) => {
|
||||||
|
return (
|
||||||
|
rect1.x === rect2.x &&
|
||||||
|
rect1.y === rect2.y &&
|
||||||
|
rect1.width === rect2.width &&
|
||||||
|
rect1.height === rect2.height
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
retry({delay: RETRY_DELAY}),
|
||||||
|
ignoreElements()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the element is in the viewport and auto-scrolls it if it is not.
|
||||||
|
*/
|
||||||
|
#ensureElementIsInTheViewportIfNeeded = <ElementType extends Element>(
|
||||||
|
handle: HandleFor<ElementType>
|
||||||
|
): Observable<never> => {
|
||||||
|
if (!this.#ensureElementIsInTheViewport) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
return from(handle.isIntersectingViewport({threshold: 0})).pipe(
|
||||||
|
filter(isIntersectingViewport => {
|
||||||
|
return !isIntersectingViewport;
|
||||||
|
}),
|
||||||
|
mergeMap(() => {
|
||||||
|
return from(handle.scrollIntoView());
|
||||||
|
}),
|
||||||
|
mergeMap(() => {
|
||||||
|
return defer(() => {
|
||||||
|
return from(handle.isIntersectingViewport({threshold: 0}));
|
||||||
|
}).pipe(first(identity), retry({delay: RETRY_DELAY}), ignoreElements());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
#click<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<LocatorClickOptions>
|
options?: Readonly<LocatorClickOptions>
|
||||||
): Promise<void>;
|
): Observable<void> {
|
||||||
|
const signal = options?.signal;
|
||||||
|
return this._wait(options).pipe(
|
||||||
|
this.operators.conditions(
|
||||||
|
[
|
||||||
|
this.#ensureElementIsInTheViewportIfNeeded,
|
||||||
|
this.#waitForStableBoundingBoxIfNeeded,
|
||||||
|
this.#waitForEnabledIfNeeded,
|
||||||
|
],
|
||||||
|
signal
|
||||||
|
),
|
||||||
|
tap(() => {
|
||||||
|
return this.emit(LocatorEmittedEvents.Action);
|
||||||
|
}),
|
||||||
|
mergeMap(handle => {
|
||||||
|
return from(handle.click(options)).pipe(
|
||||||
|
catchError((_, caught) => {
|
||||||
|
void handle.dispose().catch(debugError);
|
||||||
|
return caught;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
this.operators.retryAndRaceWithSignalAndTimer(signal)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#fill<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
value: string,
|
||||||
|
options?: Readonly<ActionOptions>
|
||||||
|
): Observable<void> {
|
||||||
|
const signal = options?.signal;
|
||||||
|
return this._wait(options).pipe(
|
||||||
|
this.operators.conditions(
|
||||||
|
[
|
||||||
|
this.#ensureElementIsInTheViewportIfNeeded,
|
||||||
|
this.#waitForStableBoundingBoxIfNeeded,
|
||||||
|
this.#waitForEnabledIfNeeded,
|
||||||
|
],
|
||||||
|
signal
|
||||||
|
),
|
||||||
|
tap(() => {
|
||||||
|
return this.emit(LocatorEmittedEvents.Action);
|
||||||
|
}),
|
||||||
|
mergeMap(handle => {
|
||||||
|
return from(
|
||||||
|
(handle as unknown as ElementHandle<HTMLElement>).evaluate(el => {
|
||||||
|
if (el instanceof HTMLSelectElement) {
|
||||||
|
return 'select';
|
||||||
|
}
|
||||||
|
if (el instanceof HTMLInputElement) {
|
||||||
|
if (
|
||||||
|
new Set([
|
||||||
|
'textarea',
|
||||||
|
'text',
|
||||||
|
'url',
|
||||||
|
'tel',
|
||||||
|
'search',
|
||||||
|
'password',
|
||||||
|
'number',
|
||||||
|
'email',
|
||||||
|
]).has(el.type)
|
||||||
|
) {
|
||||||
|
return 'typeable-input';
|
||||||
|
} else {
|
||||||
|
return 'other-input';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.isContentEditable) {
|
||||||
|
return 'contenteditable';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'unknown';
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
mergeMap(inputType => {
|
||||||
|
switch (inputType) {
|
||||||
|
case 'select':
|
||||||
|
return from(handle.select(value).then(noop));
|
||||||
|
case 'contenteditable':
|
||||||
|
case 'typeable-input':
|
||||||
|
return from(
|
||||||
|
(
|
||||||
|
handle as unknown as ElementHandle<HTMLInputElement>
|
||||||
|
).evaluate((input, newValue) => {
|
||||||
|
const currentValue = input.isContentEditable
|
||||||
|
? input.innerText
|
||||||
|
: input.value;
|
||||||
|
|
||||||
|
// Clear the input if the current value does not match the filled
|
||||||
|
// out value.
|
||||||
|
if (
|
||||||
|
newValue.length <= currentValue.length ||
|
||||||
|
!newValue.startsWith(input.value)
|
||||||
|
) {
|
||||||
|
if (input.isContentEditable) {
|
||||||
|
input.innerText = '';
|
||||||
|
} else {
|
||||||
|
input.value = '';
|
||||||
|
}
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
const originalValue = input.isContentEditable
|
||||||
|
? input.innerText
|
||||||
|
: input.value;
|
||||||
|
|
||||||
|
// If the value is partially filled out, only type the rest. Move
|
||||||
|
// cursor to the end of the common prefix.
|
||||||
|
if (input.isContentEditable) {
|
||||||
|
input.innerText = '';
|
||||||
|
input.innerText = originalValue;
|
||||||
|
} else {
|
||||||
|
input.value = '';
|
||||||
|
input.value = originalValue;
|
||||||
|
}
|
||||||
|
return newValue.substring(originalValue.length);
|
||||||
|
}, value)
|
||||||
|
).pipe(
|
||||||
|
mergeMap(textToType => {
|
||||||
|
return from(handle.type(textToType));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
case 'other-input':
|
||||||
|
return from(handle.focus()).pipe(
|
||||||
|
mergeMap(() => {
|
||||||
|
return from(
|
||||||
|
handle.evaluate((input, value) => {
|
||||||
|
(input as HTMLInputElement).value = value;
|
||||||
|
input.dispatchEvent(
|
||||||
|
new Event('input', {bubbles: true})
|
||||||
|
);
|
||||||
|
input.dispatchEvent(
|
||||||
|
new Event('change', {bubbles: true})
|
||||||
|
);
|
||||||
|
}, value)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
case 'unknown':
|
||||||
|
throw new Error(`Element cannot be filled out.`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
catchError((_, caught) => {
|
||||||
|
void handle.dispose().catch(debugError);
|
||||||
|
return caught;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
this.operators.retryAndRaceWithSignalAndTimer(signal)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#hover<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
options?: Readonly<ActionOptions>
|
||||||
|
): Observable<void> {
|
||||||
|
const signal = options?.signal;
|
||||||
|
return this._wait(options).pipe(
|
||||||
|
this.operators.conditions(
|
||||||
|
[
|
||||||
|
this.#ensureElementIsInTheViewportIfNeeded,
|
||||||
|
this.#waitForStableBoundingBoxIfNeeded,
|
||||||
|
],
|
||||||
|
signal
|
||||||
|
),
|
||||||
|
tap(() => {
|
||||||
|
return this.emit(LocatorEmittedEvents.Action);
|
||||||
|
}),
|
||||||
|
mergeMap(handle => {
|
||||||
|
return from(handle.hover()).pipe(
|
||||||
|
catchError((_, caught) => {
|
||||||
|
void handle.dispose().catch(debugError);
|
||||||
|
return caught;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
this.operators.retryAndRaceWithSignalAndTimer(signal)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
options?: Readonly<LocatorScrollOptions>
|
||||||
|
): Observable<void> {
|
||||||
|
const signal = options?.signal;
|
||||||
|
return this._wait(options).pipe(
|
||||||
|
this.operators.conditions(
|
||||||
|
[
|
||||||
|
this.#ensureElementIsInTheViewportIfNeeded,
|
||||||
|
this.#waitForStableBoundingBoxIfNeeded,
|
||||||
|
],
|
||||||
|
signal
|
||||||
|
),
|
||||||
|
tap(() => {
|
||||||
|
return this.emit(LocatorEmittedEvents.Action);
|
||||||
|
}),
|
||||||
|
mergeMap(handle => {
|
||||||
|
return from(
|
||||||
|
handle.evaluate(
|
||||||
|
(el, scrollTop, scrollLeft) => {
|
||||||
|
if (scrollTop !== undefined) {
|
||||||
|
el.scrollTop = scrollTop;
|
||||||
|
}
|
||||||
|
if (scrollLeft !== undefined) {
|
||||||
|
el.scrollLeft = scrollLeft;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options?.scrollTop,
|
||||||
|
options?.scrollLeft
|
||||||
|
)
|
||||||
|
).pipe(
|
||||||
|
catchError((_, caught) => {
|
||||||
|
void handle.dispose().catch(debugError);
|
||||||
|
return caught;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
this.operators.retryAndRaceWithSignalAndTimer(signal)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
abstract _wait(options?: Readonly<ActionOptions>): Observable<HandleFor<T>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an expectation that is evaluated against located values.
|
||||||
|
*
|
||||||
|
* If the expectations do not match, then the locator will retry.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
expect<S extends T>(predicate: Predicate<T, S>): Locator<S> {
|
||||||
|
return new ExpectedLocator(this, predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
click<ElementType extends Element>(
|
||||||
|
this: Locator<ElementType>,
|
||||||
|
options?: Readonly<LocatorClickOptions>
|
||||||
|
): Promise<void> {
|
||||||
|
return firstValueFrom(this.#click(options));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fills out the input identified by the locator using the provided value. The
|
* Fills out the input identified by the locator using the provided value. The
|
||||||
@ -183,19 +659,25 @@ export abstract class Locator<T> extends EventEmitter {
|
|||||||
* method is chosen based on the type. contenteditable, selector, inputs are
|
* method is chosen based on the type. contenteditable, selector, inputs are
|
||||||
* supported.
|
* supported.
|
||||||
*/
|
*/
|
||||||
abstract fill<ElementType extends Element>(
|
fill<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
value: string,
|
value: string,
|
||||||
options?: Readonly<ActionOptions>
|
options?: Readonly<ActionOptions>
|
||||||
): Promise<void>;
|
): Promise<void> {
|
||||||
|
return firstValueFrom(this.#fill(value, options));
|
||||||
|
}
|
||||||
|
|
||||||
abstract hover<ElementType extends Element>(
|
hover<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<ActionOptions>
|
options?: Readonly<ActionOptions>
|
||||||
): Promise<void>;
|
): Promise<void> {
|
||||||
|
return firstValueFrom(this.#hover(options));
|
||||||
|
}
|
||||||
|
|
||||||
abstract scroll<ElementType extends Element>(
|
scroll<ElementType extends Element>(
|
||||||
this: Locator<ElementType>,
|
this: Locator<ElementType>,
|
||||||
options?: Readonly<LocatorScrollOptions>
|
options?: Readonly<LocatorScrollOptions>
|
||||||
): Promise<void>;
|
): Promise<void> {
|
||||||
|
return firstValueFrom(this.#scroll(options));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,49 @@
|
|||||||
import {TimeoutError} from '../../common/Errors.js';
|
/**
|
||||||
import {Awaitable, HandleFor, NodeFor} from '../../common/types.js';
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
import {debugError} from '../../common/util.js';
|
*
|
||||||
import {isErrorLike} from '../../util/ErrorLike.js';
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
import {BoundingBox, ElementHandle} from '../ElementHandle.js';
|
* you may not use this file except in compliance with the License.
|
||||||
import type {Frame} from '../Frame.js';
|
* You may obtain a copy of the License at
|
||||||
import type {Page} from '../Page.js';
|
*
|
||||||
|
* 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 {
|
import {
|
||||||
ActionOptions,
|
EMPTY,
|
||||||
LOCATOR_CONTEXTS,
|
Observable,
|
||||||
Locator,
|
defer,
|
||||||
LocatorClickOptions,
|
filter,
|
||||||
LocatorEmittedEvents,
|
first,
|
||||||
LocatorScrollOptions,
|
from,
|
||||||
VisibilityOption,
|
identity,
|
||||||
} from './locators.js';
|
ignoreElements,
|
||||||
|
retry,
|
||||||
|
throwIfEmpty,
|
||||||
|
} from '../../../third_party/rxjs/rxjs.js';
|
||||||
|
import {HandleFor, NodeFor} from '../../common/types.js';
|
||||||
|
import {Frame} from '../Frame.js';
|
||||||
|
import {Page} from '../Page.js';
|
||||||
|
|
||||||
/**
|
import {ActionOptions, Locator, RETRY_DELAY} from './locators.js';
|
||||||
* Timeout for individual operations inside the locator. On errors the
|
|
||||||
* operation is retried as long as {@link Locator.setTimeout} is not
|
|
||||||
* exceeded. This timeout should be generally much lower as locating an
|
|
||||||
* element means multiple asynchronious operations.
|
|
||||||
*/
|
|
||||||
const CONDITION_TIMEOUT = 1000;
|
|
||||||
const WAIT_FOR_FUNCTION_DELAY = 100;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export type ActionCondition<T> = (
|
export type Action<T, U> = (
|
||||||
element: HandleFor<T>,
|
element: HandleFor<T>,
|
||||||
signal: AbortSignal
|
signal?: AbortSignal
|
||||||
) => Promise<void>;
|
) => Observable<U>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class NodeLocator<T extends Node> extends Locator<T> {
|
export class NodeLocator<T extends Node> extends Locator<T> {
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
static create<Selector extends string>(
|
static create<Selector extends string>(
|
||||||
pageOrFrame: Page | Frame,
|
pageOrFrame: Page | Frame,
|
||||||
selector: Selector
|
selector: Selector
|
||||||
@ -53,447 +57,55 @@ export class NodeLocator<T extends Node> extends Locator<T> {
|
|||||||
|
|
||||||
#pageOrFrame: Page | Frame;
|
#pageOrFrame: Page | Frame;
|
||||||
#selector: string;
|
#selector: string;
|
||||||
#visibility: VisibilityOption = 'visible';
|
|
||||||
#timeout = 30000;
|
|
||||||
#ensureElementIsInTheViewport = true;
|
|
||||||
#waitForEnabled = true;
|
|
||||||
#waitForStableBoundingBox = true;
|
|
||||||
|
|
||||||
constructor(pageOrFrame: Page | Frame, selector: string) {
|
private constructor(pageOrFrame: Page | Frame, selector: string) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.#pageOrFrame = pageOrFrame;
|
this.#pageOrFrame = pageOrFrame;
|
||||||
this.#selector = selector;
|
this.#selector = selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVisibility(visibility: VisibilityOption): this {
|
|
||||||
this.#visibility = visibility;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(timeout: number): this {
|
|
||||||
this.#timeout = timeout;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEnsureElementIsInTheViewport(value: boolean): this {
|
|
||||||
this.#ensureElementIsInTheViewport = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setWaitForEnabled(value: boolean): this {
|
|
||||||
this.#waitForEnabled = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
setWaitForStableBoundingBox(value: boolean): this {
|
|
||||||
this.#waitForStableBoundingBox = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retries the `fn` until a truthy result is returned.
|
|
||||||
*/
|
|
||||||
async #waitForFunction<T>(
|
|
||||||
fn: (signal: AbortSignal) => Awaitable<T>,
|
|
||||||
signal?: AbortSignal,
|
|
||||||
timeout = CONDITION_TIMEOUT
|
|
||||||
): Promise<T> {
|
|
||||||
let isActive = true;
|
|
||||||
let controller: AbortController;
|
|
||||||
// If the loop times out, we abort only the last iteration's controller.
|
|
||||||
const timeoutId = timeout
|
|
||||||
? setTimeout(() => {
|
|
||||||
isActive = false;
|
|
||||||
controller?.abort();
|
|
||||||
}, timeout)
|
|
||||||
: 0;
|
|
||||||
// If the user's signal aborts, we abort the last iteration and the loop.
|
|
||||||
signal?.addEventListener(
|
|
||||||
'abort',
|
|
||||||
() => {
|
|
||||||
controller?.abort();
|
|
||||||
isActive = false;
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
},
|
|
||||||
{once: true}
|
|
||||||
);
|
|
||||||
while (isActive) {
|
|
||||||
controller = new AbortController();
|
|
||||||
try {
|
|
||||||
const result = await fn(controller.signal);
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
return result;
|
|
||||||
} catch (err) {
|
|
||||||
if (isErrorLike(err)) {
|
|
||||||
debugError(err);
|
|
||||||
// Retry on all timeouts.
|
|
||||||
if (err instanceof TimeoutError) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Abort error are ignored as they only affect one iteration.
|
|
||||||
if (err.name === 'AbortError') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
} finally {
|
|
||||||
// We abort any operations that might have been started by `fn`, because
|
|
||||||
// the iteration is now over.
|
|
||||||
controller.abort();
|
|
||||||
}
|
|
||||||
await new Promise(resolve => {
|
|
||||||
return setTimeout(resolve, WAIT_FOR_FUNCTION_DELAY);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
signal?.throwIfAborted();
|
|
||||||
throw new TimeoutError(
|
|
||||||
`waitForFunction timed out. The timeout is ${timeout}ms.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the element is in the viewport and auto-scrolls it if it is not.
|
|
||||||
*/
|
|
||||||
#ensureElementIsInTheViewportIfNeeded = async <ElementType extends Element>(
|
|
||||||
element: HandleFor<ElementType>,
|
|
||||||
signal?: AbortSignal
|
|
||||||
): Promise<void> => {
|
|
||||||
if (!this.#ensureElementIsInTheViewport) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Side-effect: this also checks if it is connected.
|
|
||||||
const isIntersectingViewport = await element.isIntersectingViewport({
|
|
||||||
threshold: 0,
|
|
||||||
});
|
|
||||||
signal?.throwIfAborted();
|
|
||||||
if (!isIntersectingViewport) {
|
|
||||||
await element.scrollIntoView();
|
|
||||||
signal?.throwIfAborted();
|
|
||||||
await this.#waitForFunction(async () => {
|
|
||||||
return await element.isIntersectingViewport({
|
|
||||||
threshold: 0,
|
|
||||||
});
|
|
||||||
}, signal);
|
|
||||||
signal?.throwIfAborted();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for the element to become visible or hidden. visibility === 'visible'
|
* Waits for the element to become visible or hidden. visibility === 'visible'
|
||||||
* means that the element has a computed style, the visibility property other
|
* means that the element has a computed style, the visibility property other
|
||||||
* than 'hidden' or 'collapse' and non-empty bounding box. visibility ===
|
* than 'hidden' or 'collapse' and non-empty bounding box. visibility ===
|
||||||
* 'hidden' means the opposite of that.
|
* 'hidden' means the opposite of that.
|
||||||
*/
|
*/
|
||||||
#waitForVisibilityIfNeeded = async <ElementType extends Element>(
|
#waitForVisibilityIfNeeded = (handle: HandleFor<T>): Observable<never> => {
|
||||||
element: HandleFor<ElementType>,
|
if (!this.visibility) {
|
||||||
signal?: AbortSignal
|
return EMPTY;
|
||||||
): Promise<void> => {
|
|
||||||
if (this.#visibility === null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (this.#visibility === 'hidden') {
|
|
||||||
await this.#waitForFunction(async () => {
|
|
||||||
return element.isHidden();
|
|
||||||
}, signal);
|
|
||||||
}
|
|
||||||
await this.#waitForFunction(async () => {
|
|
||||||
return element.isVisible();
|
|
||||||
}, signal);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
return (() => {
|
||||||
* If the element has a "disabled" property, wait for the element to be
|
switch (this.visibility) {
|
||||||
* enabled.
|
case 'hidden':
|
||||||
*/
|
return defer(() => {
|
||||||
#waitForEnabledIfNeeded = async <ElementType extends Element>(
|
return from(handle.isHidden());
|
||||||
element: HandleFor<ElementType>,
|
|
||||||
signal?: AbortSignal
|
|
||||||
): Promise<void> => {
|
|
||||||
if (!this.#waitForEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.#pageOrFrame.waitForFunction(
|
|
||||||
el => {
|
|
||||||
if ('disabled' in el && typeof el.disabled === 'boolean') {
|
|
||||||
return !el.disabled;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timeout: CONDITION_TIMEOUT,
|
|
||||||
signal,
|
|
||||||
},
|
|
||||||
element
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares the bounding box of the element for two consecutive animation
|
|
||||||
* frames and waits till they are the same.
|
|
||||||
*/
|
|
||||||
#waitForStableBoundingBoxIfNeeded = async <ElementType extends Element>(
|
|
||||||
element: HandleFor<ElementType>,
|
|
||||||
signal?: AbortSignal
|
|
||||||
): Promise<void> => {
|
|
||||||
if (!this.#waitForStableBoundingBox) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
function getClientRect() {
|
|
||||||
return element.evaluate(el => {
|
|
||||||
return new Promise<[BoundingBox, BoundingBox]>(resolve => {
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
const rect1 = el.getBoundingClientRect();
|
|
||||||
window.requestAnimationFrame(() => {
|
|
||||||
const rect2 = el.getBoundingClientRect();
|
|
||||||
resolve([
|
|
||||||
{
|
|
||||||
x: rect1.x,
|
|
||||||
y: rect1.y,
|
|
||||||
width: rect1.width,
|
|
||||||
height: rect1.height,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x: rect2.x,
|
|
||||||
y: rect2.y,
|
|
||||||
width: rect2.width,
|
|
||||||
height: rect2.height,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
case 'visible':
|
||||||
});
|
return defer(() => {
|
||||||
}
|
return from(handle.isVisible());
|
||||||
await this.#waitForFunction(async () => {
|
});
|
||||||
const [rect1, rect2] = await getClientRect();
|
}
|
||||||
return (
|
})().pipe(first(identity), retry({delay: RETRY_DELAY}), ignoreElements());
|
||||||
rect1.x === rect2.x &&
|
|
||||||
rect1.y === rect2.y &&
|
|
||||||
rect1.width === rect2.width &&
|
|
||||||
rect1.height === rect2.height
|
|
||||||
);
|
|
||||||
}, signal);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#run<U>(
|
_wait(options?: Readonly<ActionOptions>): Observable<HandleFor<T>> {
|
||||||
action: (el: HandleFor<T>) => Promise<U>,
|
const signal = options?.signal;
|
||||||
signal?: AbortSignal,
|
return defer(() => {
|
||||||
conditions: Array<ActionCondition<T>> = []
|
return from(
|
||||||
): Promise<U> {
|
this.#pageOrFrame.waitForSelector(this.#selector, {
|
||||||
const globalConditions = [
|
visible: false,
|
||||||
...(LOCATOR_CONTEXTS.get(this)?.conditions?.values() ?? []),
|
timeout: this.timeout,
|
||||||
] as Array<ActionCondition<T>>;
|
signal,
|
||||||
const allConditions = conditions.concat(globalConditions);
|
}) as Promise<HandleFor<T> | null>
|
||||||
return this.#waitForFunction(
|
);
|
||||||
async signal => {
|
}).pipe(
|
||||||
// 1. Select the element without visibility checks.
|
filter((value): value is NonNullable<typeof value> => {
|
||||||
const element = (await this.#pageOrFrame.waitForSelector(
|
return value !== null;
|
||||||
this.#selector,
|
}),
|
||||||
{
|
throwIfEmpty(),
|
||||||
visible: false,
|
this.operators.conditions([this.#waitForVisibilityIfNeeded], signal)
|
||||||
timeout: this.#timeout,
|
|
||||||
signal,
|
|
||||||
}
|
|
||||||
)) as HandleFor<T> | null;
|
|
||||||
// Retry if no element is found.
|
|
||||||
if (!element) {
|
|
||||||
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 {
|
|
||||||
return await action(element);
|
|
||||||
} catch (error) {
|
|
||||||
void element.dispose().catch(debugError);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
signal,
|
|
||||||
this.#timeout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async click<ElementType extends Element>(
|
|
||||||
this: NodeLocator<ElementType>,
|
|
||||||
options?: Readonly<LocatorClickOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
await this.#run(
|
|
||||||
async element => {
|
|
||||||
await element.click(options);
|
|
||||||
void element.dispose().catch(debugError);
|
|
||||||
},
|
|
||||||
options?.signal,
|
|
||||||
[
|
|
||||||
this.#ensureElementIsInTheViewportIfNeeded,
|
|
||||||
this.#waitForVisibilityIfNeeded,
|
|
||||||
this.#waitForEnabledIfNeeded,
|
|
||||||
this.#waitForStableBoundingBoxIfNeeded,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills out the input identified by the locator using the provided value. The
|
|
||||||
* type of the input is determined at runtime and the appropriate fill-out
|
|
||||||
* method is chosen based on the type. contenteditable, selector, inputs are
|
|
||||||
* supported.
|
|
||||||
*/
|
|
||||||
async fill<ElementType extends Element>(
|
|
||||||
this: NodeLocator<ElementType>,
|
|
||||||
value: string,
|
|
||||||
options?: Readonly<ActionOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
await this.#run(
|
|
||||||
async element => {
|
|
||||||
const input = element as unknown as ElementHandle<HTMLElement>;
|
|
||||||
const inputType = await input.evaluate(el => {
|
|
||||||
if (el instanceof HTMLSelectElement) {
|
|
||||||
return 'select';
|
|
||||||
}
|
|
||||||
if (el instanceof HTMLInputElement) {
|
|
||||||
if (
|
|
||||||
new Set([
|
|
||||||
'textarea',
|
|
||||||
'text',
|
|
||||||
'url',
|
|
||||||
'tel',
|
|
||||||
'search',
|
|
||||||
'password',
|
|
||||||
'number',
|
|
||||||
'email',
|
|
||||||
]).has(el.type)
|
|
||||||
) {
|
|
||||||
return 'typeable-input';
|
|
||||||
} else {
|
|
||||||
return 'other-input';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (el.isContentEditable) {
|
|
||||||
return 'contenteditable';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'unknown';
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (inputType) {
|
|
||||||
case 'select':
|
|
||||||
await input.select(value);
|
|
||||||
break;
|
|
||||||
case 'contenteditable':
|
|
||||||
case 'typeable-input':
|
|
||||||
const textToType = await (
|
|
||||||
input as ElementHandle<HTMLInputElement>
|
|
||||||
).evaluate((input, newValue) => {
|
|
||||||
const currentValue = input.isContentEditable
|
|
||||||
? input.innerText
|
|
||||||
: input.value;
|
|
||||||
|
|
||||||
// Clear the input if the current value does not match the filled
|
|
||||||
// out value.
|
|
||||||
if (
|
|
||||||
newValue.length <= currentValue.length ||
|
|
||||||
!newValue.startsWith(input.value)
|
|
||||||
) {
|
|
||||||
if (input.isContentEditable) {
|
|
||||||
input.innerText = '';
|
|
||||||
} else {
|
|
||||||
input.value = '';
|
|
||||||
}
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
const originalValue = input.isContentEditable
|
|
||||||
? input.innerText
|
|
||||||
: input.value;
|
|
||||||
|
|
||||||
// If the value is partially filled out, only type the rest. Move
|
|
||||||
// cursor to the end of the common prefix.
|
|
||||||
if (input.isContentEditable) {
|
|
||||||
input.innerText = '';
|
|
||||||
input.innerText = originalValue;
|
|
||||||
} else {
|
|
||||||
input.value = '';
|
|
||||||
input.value = originalValue;
|
|
||||||
}
|
|
||||||
return newValue.substring(originalValue.length);
|
|
||||||
}, value);
|
|
||||||
await input.type(textToType);
|
|
||||||
break;
|
|
||||||
case 'other-input':
|
|
||||||
await input.focus();
|
|
||||||
await input.evaluate((input, value) => {
|
|
||||||
(input as HTMLInputElement).value = value;
|
|
||||||
input.dispatchEvent(new Event('input', {bubbles: true}));
|
|
||||||
input.dispatchEvent(new Event('change', {bubbles: true}));
|
|
||||||
}, value);
|
|
||||||
break;
|
|
||||||
case 'unknown':
|
|
||||||
throw new Error(`Element cannot be filled out.`);
|
|
||||||
}
|
|
||||||
void element.dispose().catch(debugError);
|
|
||||||
},
|
|
||||||
options?.signal,
|
|
||||||
[
|
|
||||||
this.#ensureElementIsInTheViewportIfNeeded,
|
|
||||||
this.#waitForVisibilityIfNeeded,
|
|
||||||
this.#waitForEnabledIfNeeded,
|
|
||||||
this.#waitForStableBoundingBoxIfNeeded,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async hover<ElementType extends Element>(
|
|
||||||
this: NodeLocator<ElementType>,
|
|
||||||
options?: Readonly<ActionOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
await this.#run(
|
|
||||||
async element => {
|
|
||||||
await element.hover();
|
|
||||||
void element.dispose().catch(debugError);
|
|
||||||
},
|
|
||||||
options?.signal,
|
|
||||||
[
|
|
||||||
this.#ensureElementIsInTheViewportIfNeeded,
|
|
||||||
this.#waitForVisibilityIfNeeded,
|
|
||||||
this.#waitForStableBoundingBoxIfNeeded,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async scroll<ElementType extends Element>(
|
|
||||||
this: NodeLocator<ElementType>,
|
|
||||||
options?: Readonly<LocatorScrollOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
await this.#run(
|
|
||||||
async element => {
|
|
||||||
await element.evaluate(
|
|
||||||
(el, scrollTop, scrollLeft) => {
|
|
||||||
if (scrollTop !== undefined) {
|
|
||||||
el.scrollTop = scrollTop;
|
|
||||||
}
|
|
||||||
if (scrollLeft !== undefined) {
|
|
||||||
el.scrollLeft = scrollLeft;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
options?.scrollTop,
|
|
||||||
options?.scrollLeft
|
|
||||||
);
|
|
||||||
void element.dispose().catch(debugError);
|
|
||||||
},
|
|
||||||
options?.signal,
|
|
||||||
[
|
|
||||||
this.#ensureElementIsInTheViewportIfNeeded,
|
|
||||||
this.#waitForVisibilityIfNeeded,
|
|
||||||
this.#waitForStableBoundingBoxIfNeeded,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,174 +1,63 @@
|
|||||||
import {isErrorLike} from '../../util/ErrorLike.js';
|
/**
|
||||||
|
* 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 {
|
import {Observable, race} from '../../../third_party/rxjs/rxjs.js';
|
||||||
Locator,
|
import {HandleFor} from '../../puppeteer-core.js';
|
||||||
VisibilityOption,
|
|
||||||
LocatorEmittedEvents,
|
import {ActionOptions, Locator} from './locators.js';
|
||||||
LocatorClickOptions,
|
|
||||||
ActionOptions,
|
|
||||||
LocatorScrollOptions,
|
|
||||||
} from './locators.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export type UnionLocatorOf<T> = T extends Array<Locator<infer S>> ? S : never;
|
export type AwaitedLocator<T> = T extends Locator<infer S> ? S : never;
|
||||||
|
|
||||||
|
function checkLocatorArray<T extends readonly unknown[] | []>(
|
||||||
|
locators: T
|
||||||
|
): ReadonlyArray<Locator<AwaitedLocator<T[number]>>> {
|
||||||
|
for (const locator of locators) {
|
||||||
|
if (!(locator instanceof Locator)) {
|
||||||
|
throw new Error('Unknown locator for race candidate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locators as ReadonlyArray<Locator<AwaitedLocator<T[number]>>>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class RaceLocator<T> extends Locator<T> {
|
export class RaceLocator<T> extends Locator<T> {
|
||||||
#locators: Array<Locator<T>>;
|
static create<T extends readonly unknown[]>(
|
||||||
|
locators: T
|
||||||
|
): Locator<AwaitedLocator<T[number]>> {
|
||||||
|
const array = checkLocatorArray(locators);
|
||||||
|
return new RaceLocator(array);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(locators: Array<Locator<T>>) {
|
#locators: ReadonlyArray<Locator<T>>;
|
||||||
|
|
||||||
|
constructor(locators: ReadonlyArray<Locator<T>>) {
|
||||||
super();
|
super();
|
||||||
this.#locators = locators;
|
this.#locators = locators;
|
||||||
}
|
}
|
||||||
|
|
||||||
override setVisibility(visibility: VisibilityOption): this {
|
override _wait(options?: Readonly<ActionOptions>): Observable<HandleFor<T>> {
|
||||||
for (const locator of this.#locators) {
|
return race(
|
||||||
locator.setVisibility(visibility);
|
...this.#locators.map(locator => {
|
||||||
}
|
return locator._wait(options);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
override setTimeout(timeout: number): this {
|
|
||||||
for (const locator of this.#locators) {
|
|
||||||
locator.setTimeout(timeout);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
override setEnsureElementIsInTheViewport(value: boolean): this {
|
|
||||||
for (const locator of this.#locators) {
|
|
||||||
locator.setEnsureElementIsInTheViewport(value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
override setWaitForEnabled(value: boolean): this {
|
|
||||||
for (const locator of this.#locators) {
|
|
||||||
locator.setWaitForEnabled(value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
override setWaitForStableBoundingBox(value: boolean): this {
|
|
||||||
for (const locator of this.#locators) {
|
|
||||||
locator.setWaitForStableBoundingBox(value);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
async #run(
|
|
||||||
action: (locator: Locator<T>, signal: AbortSignal) => Promise<void>,
|
|
||||||
signal?: AbortSignal
|
|
||||||
) {
|
|
||||||
const abortControllers = new WeakMap<Locator<T>, AbortController>();
|
|
||||||
|
|
||||||
// Abort all locators if the user-provided signal aborts.
|
|
||||||
signal?.addEventListener('abort', () => {
|
|
||||||
for (const locator of this.#locators) {
|
|
||||||
abortControllers.get(locator)?.abort();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleLocatorAction = (locator: Locator<T>): (() => void) => {
|
|
||||||
return () => {
|
|
||||||
// When one locator is ready to act, we will abort other locators.
|
|
||||||
for (const other of this.#locators) {
|
|
||||||
if (other !== locator) {
|
|
||||||
abortControllers.get(other)?.abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.emit(LocatorEmittedEvents.Action);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const createAbortController = (locator: Locator<T>): AbortController => {
|
|
||||||
const abortController = new AbortController();
|
|
||||||
abortControllers.set(locator, abortController);
|
|
||||||
return abortController;
|
|
||||||
};
|
|
||||||
|
|
||||||
const results = await Promise.allSettled(
|
|
||||||
this.#locators.map(locator => {
|
|
||||||
return action(
|
|
||||||
locator.on(LocatorEmittedEvents.Action, handleLocatorAction(locator)),
|
|
||||||
createAbortController(locator).signal
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
signal?.throwIfAborted();
|
|
||||||
|
|
||||||
const rejected = results.filter(
|
|
||||||
(result): result is PromiseRejectedResult => {
|
|
||||||
return result.status === 'rejected';
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// If some locators are fulfilled, do not throw.
|
|
||||||
if (rejected.length !== results.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const result of rejected) {
|
|
||||||
const reason = result.reason;
|
|
||||||
// AbortError is be an expected result of a race.
|
|
||||||
if (isErrorLike(reason) && reason.name === 'AbortError') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async click<ElementType extends Element>(
|
|
||||||
this: RaceLocator<ElementType>,
|
|
||||||
options?: Readonly<LocatorClickOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
return await this.#run(
|
|
||||||
(locator, signal) => {
|
|
||||||
return locator.click({...options, signal});
|
|
||||||
},
|
|
||||||
options?.signal
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fill<ElementType extends Element>(
|
|
||||||
this: RaceLocator<ElementType>,
|
|
||||||
value: string,
|
|
||||||
options?: Readonly<ActionOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
return await this.#run(
|
|
||||||
(locator, signal) => {
|
|
||||||
return locator.fill(value, {...options, signal});
|
|
||||||
},
|
|
||||||
options?.signal
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async hover<ElementType extends Element>(
|
|
||||||
this: RaceLocator<ElementType>,
|
|
||||||
options?: Readonly<ActionOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
return await this.#run(
|
|
||||||
(locator, signal) => {
|
|
||||||
return locator.hover({...options, signal});
|
|
||||||
},
|
|
||||||
options?.signal
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async scroll<ElementType extends Element>(
|
|
||||||
this: RaceLocator<ElementType>,
|
|
||||||
options?: Readonly<LocatorScrollOptions>
|
|
||||||
): Promise<void> {
|
|
||||||
return await this.#run(
|
|
||||||
(locator, signal) => {
|
|
||||||
return locator.scroll({...options, signal});
|
|
||||||
},
|
|
||||||
options?.signal
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
export * from './Locator.js';
|
export * from './Locator.js';
|
||||||
export * from './NodeLocator.js';
|
export * from './NodeLocator.js';
|
||||||
export * from './ExpectedLocator.js';
|
export * from './ExpectedLocator.js';
|
||||||
|
41
packages/puppeteer-core/third_party/rxjs/rxjs.ts
vendored
Normal file
41
packages/puppeteer-core/third_party/rxjs/rxjs.ts
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
export {
|
||||||
|
catchError,
|
||||||
|
defaultIfEmpty,
|
||||||
|
filter,
|
||||||
|
first,
|
||||||
|
ignoreElements,
|
||||||
|
map,
|
||||||
|
mergeMap,
|
||||||
|
raceWith,
|
||||||
|
retry,
|
||||||
|
tap,
|
||||||
|
throwIfEmpty,
|
||||||
|
firstValueFrom,
|
||||||
|
defer,
|
||||||
|
EMPTY,
|
||||||
|
from,
|
||||||
|
fromEvent,
|
||||||
|
merge,
|
||||||
|
race,
|
||||||
|
timer,
|
||||||
|
OperatorFunction,
|
||||||
|
identity,
|
||||||
|
noop,
|
||||||
|
pipe,
|
||||||
|
Observable,
|
||||||
|
} from 'rxjs';
|
@ -236,7 +236,7 @@ describe('Locator', function () {
|
|||||||
const result = page.locator('button').click();
|
const result = page.locator('button').click();
|
||||||
clock.tick(5100);
|
clock.tick(5100);
|
||||||
await expect(result).rejects.toEqual(
|
await expect(result).rejects.toEqual(
|
||||||
new TimeoutError('waitForFunction timed out. The timeout is 5000ms.')
|
new TimeoutError('Timed out after waiting 5000ms')
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
clock.restore();
|
clock.restore();
|
||||||
@ -257,7 +257,7 @@ describe('Locator', function () {
|
|||||||
const result = page.locator('button').click();
|
const result = page.locator('button').click();
|
||||||
clock.tick(5100);
|
clock.tick(5100);
|
||||||
await expect(result).rejects.toEqual(
|
await expect(result).rejects.toEqual(
|
||||||
new TimeoutError('waitForFunction timed out. The timeout is 5000ms.')
|
new TimeoutError('Timed out after waiting 5000ms')
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
clock.restore();
|
clock.restore();
|
||||||
@ -513,15 +513,16 @@ describe('Locator', function () {
|
|||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const {page} = await getTestState();
|
const {page} = await getTestState();
|
||||||
page.setDefaultTimeout(5000);
|
|
||||||
await page.setContent(`<button>test</button>`);
|
await page.setContent(`<button>test</button>`);
|
||||||
const result = Locator.race([
|
const result = Locator.race([
|
||||||
page.locator('not-found'),
|
page.locator('not-found'),
|
||||||
page.locator('not-found'),
|
page.locator('not-found'),
|
||||||
]).click();
|
])
|
||||||
|
.setTimeout(5000)
|
||||||
|
.click();
|
||||||
clock.tick(5100);
|
clock.tick(5100);
|
||||||
await expect(result).rejects.toEqual(
|
await expect(result).rejects.toEqual(
|
||||||
new TimeoutError('waitForFunction timed out. The timeout is 5000ms.')
|
new TimeoutError('Timed out after waiting 5000ms')
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
clock.restore();
|
clock.restore();
|
||||||
|
Loading…
Reference in New Issue
Block a user