@open-wa/driver-playwright 5.0.0-alpha.7 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +2 -2
- package/README.md +2 -2
- package/dist/index.d.cts +8 -9
- package/dist/index.d.cts.map +1 -1
- package/package.json +10 -6
- package/.turbo/turbo-build.log +0 -19
- package/.turbo/turbo-lint.log +0 -6
- package/.turbo/turbo-test.log +0 -14
- package/CHANGELOG.md +0 -63
- package/src/PlaywrightBrowser.ts +0 -47
- package/src/PlaywrightDriver.ts +0 -114
- package/src/PlaywrightElementHandle.ts +0 -32
- package/src/PlaywrightPage.ts +0 -412
- package/src/__tests__/PlaywrightPage.test.ts +0 -144
- package/src/index.ts +0 -5
- package/tsconfig.json +0 -10
package/LICENSE.md
CHANGED
|
@@ -55,9 +55,9 @@ Licensor hereby grants permission by this license ("License"), free of charge,
|
|
|
55
55
|
4. Compliance with Human Rights Laws and Human Rights Principles:
|
|
56
56
|
|
|
57
57
|
a. **Human Rights Laws**. The Software shall not be used by any person or entity for any systems, activities, or other uses that violate any applicable laws, regulations, or rules that protect human, civil, labor, privacy, political, environmental, security, economic, due process, or similar rights (the "Human Rights Laws"). Where the Human Rights Laws of more than one jurisdiction are applicable to the use of the Software, the Human Rights Laws that are most protective of the individuals or groups harmed shall apply.
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
b. **Human Rights Principles**. Licensee is advised to consult the articles of the United Nations Universal Declaration of Human Rights (https://www.un.org/en/universal-declaration-human-rights/) and the United Nations Global Compact (https://www.unglobalcompact.org/ what-is-gc/mission/principles) that define recognized principles of international human rights (the "Human Rights Principles"). It is Licensor's express intent that all use of the Software be consistent with Human Rights Principles. If Licensor receives notification or otherwise learns of an alleged violation of any Human Rights Principles relating to Licensee's use of the Software, Licensor may in its discretion and without obligation (i) (a) notify Licensee of such allegation and (b) allow Licensee 90 days from notification under (i)(a) to investigate and respond to Licensor regarding the allegation and (ii) (a) after the earlier of 90 days from notification under (i)(a), or Licensee's response under (i)(b), notify Licensee of License termination and (b) allow Licensee an additional 90 days from notification under (ii)(a) to cease use of the Software.
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
c. **Indemnity**. Licensee shall hold harmless and indemnify Licensor against all losses, damages, liabilities, deficiencies, claims, actions, judgments, settlements, interest, awards, penalties, fines, costs, or expenses of whatever kind, including Licensor's reasonable attorneys' fees, arising out of or relating to Licensee's non-compliance with this License or use of the Software in violation of Human Rights Laws or Human Rights Principles.
|
|
62
62
|
|
|
63
63
|
5. Enforceability: If any portion or provision of this License is determined to be invalid, illegal, or unenforceable by a court of competent jurisdiction, then such invalidity, illegality, or unenforceability shall not affect any other term or provision of this License or invalidate or render unenforceable such term or provision in any other jurisdiction. Upon a determination that any term or provision is invalid, illegal, or unenforceable, to the extent permitted by applicable law, the court may modify this License to affect the original intent of the parties as closely as possible. The section headings are for convenience only and are not intended to affect the construction or interpretation of this License. Any rule of construction to the effect that ambiguities are to be resolved against the drafting party shall not apply in interpreting this License. The language in this License shall be interpreted as to its fair meaning and not strictly for or against any party.
|
package/README.md
CHANGED
|
@@ -12,8 +12,8 @@ pnpm add @open-wa/driver-playwright
|
|
|
12
12
|
|
|
13
13
|
## Documentation
|
|
14
14
|
|
|
15
|
-
See the [docs site](https://
|
|
15
|
+
See the [docs site](https://openwa.dev).
|
|
16
16
|
|
|
17
17
|
## License
|
|
18
18
|
|
|
19
|
-
[H-DNH
|
|
19
|
+
[H-DNH 1.1](https://github.com/open-wa/wa-automate-nodejs/blob/master/LICENSE.md) — Hippocratic + Do Not Harm
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { ConnectOptions, DisposableHandle, DriverCapabilities, IBrowser, IDriver, IDriverContext, IElementHandle, IFrame, IPage, LaunchOptions, NavigationWaitUntil, PageEventHandler, WaitForFunctionOptions } from "@open-wa/driver-interface";
|
|
2
2
|
import { Browser, CDPSession, ElementHandle, Page } from "playwright";
|
|
3
|
-
|
|
4
3
|
//#region src/PlaywrightDriver.d.ts
|
|
5
4
|
type PlaywrightBrowserType = 'chromium' | 'firefox' | 'webkit' | 'chrome' | 'msedge';
|
|
6
5
|
interface PlaywrightLaunchOptions extends LaunchOptions {
|
|
7
6
|
browser?: PlaywrightBrowserType;
|
|
8
7
|
channel?: 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary';
|
|
9
8
|
}
|
|
10
|
-
declare class PlaywrightDriver implements IDriver {
|
|
11
|
-
readonly name:
|
|
9
|
+
export declare class PlaywrightDriver implements IDriver {
|
|
10
|
+
readonly name: 'playwright';
|
|
12
11
|
version?: string;
|
|
13
12
|
readonly capabilities: DriverCapabilities;
|
|
14
13
|
private playwright;
|
|
@@ -24,11 +23,11 @@ declare class PlaywrightDriver implements IDriver {
|
|
|
24
23
|
}
|
|
25
24
|
//#endregion
|
|
26
25
|
//#region src/PlaywrightBrowser.d.ts
|
|
27
|
-
declare class PlaywrightBrowser implements IBrowser {
|
|
26
|
+
export declare class PlaywrightBrowser implements IBrowser {
|
|
28
27
|
private browser;
|
|
29
28
|
private browserType;
|
|
30
29
|
private capabilities;
|
|
31
|
-
readonly name:
|
|
30
|
+
readonly name: 'playwright';
|
|
32
31
|
constructor(browser: Browser, browserType: PlaywrightBrowserType, capabilities: DriverCapabilities);
|
|
33
32
|
newPage(): Promise<IPage>;
|
|
34
33
|
pages(): Promise<IPage[]>;
|
|
@@ -39,10 +38,10 @@ declare class PlaywrightBrowser implements IBrowser {
|
|
|
39
38
|
}
|
|
40
39
|
//#endregion
|
|
41
40
|
//#region src/PlaywrightPage.d.ts
|
|
42
|
-
declare class PlaywrightPage implements IPage {
|
|
41
|
+
export declare class PlaywrightPage implements IPage {
|
|
43
42
|
private page;
|
|
44
43
|
private capabilities;
|
|
45
|
-
readonly name:
|
|
44
|
+
readonly name: 'playwright';
|
|
46
45
|
private readonly listenerWrappers;
|
|
47
46
|
private readonly requestInterceptionRoute;
|
|
48
47
|
private requestInterceptionEnabled;
|
|
@@ -103,7 +102,7 @@ declare class PlaywrightPage implements IPage {
|
|
|
103
102
|
}
|
|
104
103
|
//#endregion
|
|
105
104
|
//#region src/PlaywrightElementHandle.d.ts
|
|
106
|
-
declare class PlaywrightElementHandle implements IElementHandle {
|
|
105
|
+
export declare class PlaywrightElementHandle implements IElementHandle {
|
|
107
106
|
private element;
|
|
108
107
|
constructor(element: ElementHandle);
|
|
109
108
|
click(): Promise<void>;
|
|
@@ -114,5 +113,5 @@ declare class PlaywrightElementHandle implements IElementHandle {
|
|
|
114
113
|
unwrap(): ElementHandle;
|
|
115
114
|
}
|
|
116
115
|
//#endregion
|
|
117
|
-
export {
|
|
116
|
+
export type { PlaywrightBrowserType, PlaywrightLaunchOptions };
|
|
118
117
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/PlaywrightDriver.ts","../src/PlaywrightBrowser.ts","../src/PlaywrightPage.ts","../src/PlaywrightElementHandle.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/PlaywrightDriver.ts","../src/PlaywrightBrowser.ts","../src/PlaywrightPage.ts","../src/PlaywrightElementHandle.ts"],"mappings":";;;KAGY;UAEK,gCAAgC;EAC7C,UAAU;EACV;;qBAGS,4BAA4B;WAC5B;EACT;WACS,cAAc;UAEf;UACA;UACA;EAER,YAAY,cAAa;EAoBnB,KAAK,MAAM,iBAAiB;EAY5B,OAAO,UAAU,0BAA0B,QAAQ;EAiCnD,QAAQ,SAAS,iBAAiB,QAAQ;EAWhD;EAIA,IAAI,kBAAkB,KAAK;EAI3B,QAAQ,kBAAkB,KAAK;;;;qBClGtB,6BAA6B;UAI1B;UACA;UACA;WALH;EAET,YACY,SAAS,SACT,aAAa,uBACb,cAAc;EAGpB,WAAW,QAAQ;EAKnB,SAAS,QAAQ;EAYjB,SAAS;EAIf;EAIM,iBAAiB;EAIvB,UAAU;;;;qBC0FD,0BAA0B;UASvB;UAEA;WAVH;mBACQ;mBACA;UAGT;EAER,YACY,MAAM,MACd,cAAc,uBACN,cAAc;EAGpB,KAAK,aAAa;IAAY,YAAY;IAAqB;MAAuB;EAS5F;EAIM,UAAU;EAIhB,aAAa;EAIP,SAAS,KAAK,KAAK,KAAK,KAAK,QAAQ,MAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ;EAI5E,eAAe,eAAe,iBAAiB,QAAQ;EAIvD,cAAc,iBAAiB,QAAQ;EAKvC,YAAY;IAAY;IAAe;MAAmB;EAI1D,aAAa,aAAa;EAI1B,uBAAuB,mBAAmB;EAgB1C,gBAAgB,kBAAkB;IAAY;MAAuB,QAAQ;EAK7E,gBAAgB,gBAAgB,UAAU,yBAAyB;EACnE,gBAAgB,KAAK,KAAK,KAAK,iBAAiB,KAAK,KAAK,UAAU,yBAAyB;EAiB7F,EAAE,mBAAmB,QAAQ;EAK7B,GAAG,mBAAmB,QAAQ;EAK9B,MAAM,mBAAmB;EAIzB,KAAK,kBAAkB,cAAc;IAAY;MAAqB;EAItE,WAAW;IAAY;IAAuB;MAAuB,QAAQ;EAQ7E,eAAe,cAAc,QAAQ,sBAAsB;EAIjE,GAAG,kBAAkB,OAAO,GAAG,cAAc,uBAAuB,iBAAiB,wBAAwB;EAiB7G,IAAI,kBAAkB,OAAO,GAAG,cAAc,uBAAuB,iBAAiB;EAchF,SAAS;EAIf;EAKA,IAAI,kBAAkB,KAAK;EAI3B,QAAQ,kBAAkB,KAAK;EAW/B,OAAO;EAOD,uBAAuB,kBAAkB;EAYzC,IAAI;IAAY;IAAe;MAAoB,QAAQ;EAO3D,aAAa;IAAY;IAAe;MAA0B;EAKlE,eAAe,QAAQ;EAY7B,UAAU;UAIF;UAgBA;UAkBA;;;;qBCvYC,mCAAmC;UACxB;EAApB,YAAoB,SAAS;EAEvB,SAAS;EAIT,KAAK,eAAe;EAIpB,aAAa,eAAe;EAK5B,eAAe;EAKf,WAAW;EAIjB,UAAU"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-wa/driver-playwright",
|
|
3
|
-
"version": "5.0.0
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Playwright driver implementation for open-wa",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"types": "./dist/index.d.cts",
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"@open-wa/driver-interface": "5.0.0",
|
|
16
|
+
"playwright": "^1.63.0"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@types/node": "^
|
|
20
|
-
"typescript": "^
|
|
21
|
-
"vitest": "
|
|
19
|
+
"@types/node": "^26.6.3",
|
|
20
|
+
"typescript": "^7.0.2",
|
|
21
|
+
"vitest": "5.0.2"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"type": "git",
|
|
28
28
|
"url": "https://github.com/open-wa/wa-automate-nodejs.git"
|
|
29
29
|
},
|
|
30
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
30
34
|
"scripts": {
|
|
31
35
|
"build": "tsdown src/index.ts --format cjs",
|
|
32
36
|
"dev": "tsdown src/index.ts --format cjs --watch",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @open-wa/driver-playwright@5.0.0-alpha.7 build /Users/Mohammed/projects/tools/wa/packages/driver-playwright
|
|
3
|
-
> tsdown src/index.ts --format cjs
|
|
4
|
-
|
|
5
|
-
ℹ tsdown v0.21.7 powered by rolldown v1.0.0-rc.12
|
|
6
|
-
ℹ entry: src/index.ts
|
|
7
|
-
ℹ tsconfig: tsconfig.json
|
|
8
|
-
ℹ Build start
|
|
9
|
-
ℹ Cleaning 3 files
|
|
10
|
-
ℹ dist/index.cjs 12.54 kB │ gzip: 3.13 kB
|
|
11
|
-
ℹ 1 files, total: 12.54 kB
|
|
12
|
-
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `tsdown:deps`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
13
|
-
|
|
14
|
-
ℹ dist/index.d.cts.map 2.99 kB │ gzip: 0.96 kB
|
|
15
|
-
ℹ dist/index.d.cts 4.72 kB │ gzip: 1.36 kB
|
|
16
|
-
ℹ 2 files, total: 7.71 kB
|
|
17
|
-
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
18
|
-
|
|
19
|
-
✔ Build complete in 12645ms
|
package/.turbo/turbo-lint.log
DELETED
package/.turbo/turbo-test.log
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @open-wa/driver-playwright@5.0.0-alpha.0 test /Users/Mohammed/projects/tools/wa/packages/driver-playwright
|
|
3
|
-
> vitest run
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
[1m[46m RUN [49m[22m [36mv4.1.2 [39m[90m/Users/Mohammed/projects/tools/wa/packages/driver-playwright[39m
|
|
7
|
-
|
|
8
|
-
[32m✓[39m src/__tests__/PlaywrightPage.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 11[2mms[22m[39m
|
|
9
|
-
|
|
10
|
-
[2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m
|
|
11
|
-
[2m Tests [22m [1m[32m3 passed[39m[22m[90m (3)[39m
|
|
12
|
-
[2m Start at [22m 15:25:43
|
|
13
|
-
[2m Duration [22m 1.27s[2m (transform 184ms, setup 0ms, import 235ms, tests 11ms, environment 0ms)[22m
|
|
14
|
-
|
package/CHANGELOG.md
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# @open-wa/driver-playwright
|
|
2
|
-
|
|
3
|
-
## 5.0.0-alpha.7
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- Updated dependencies []:
|
|
8
|
-
- @open-wa/driver-interface@5.0.0-alpha.7
|
|
9
|
-
|
|
10
|
-
## 5.0.0-alpha.6
|
|
11
|
-
|
|
12
|
-
### Patch Changes
|
|
13
|
-
|
|
14
|
-
- Updated dependencies []:
|
|
15
|
-
- @open-wa/driver-interface@5.0.0-alpha.6
|
|
16
|
-
|
|
17
|
-
## 5.0.0-alpha.5
|
|
18
|
-
|
|
19
|
-
### Patch Changes
|
|
20
|
-
|
|
21
|
-
- Updated dependencies []:
|
|
22
|
-
- @open-wa/driver-interface@5.0.0-alpha.5
|
|
23
|
-
|
|
24
|
-
## 5.0.0-alpha.4
|
|
25
|
-
|
|
26
|
-
### Patch Changes
|
|
27
|
-
|
|
28
|
-
- Updated dependencies []:
|
|
29
|
-
- @open-wa/driver-interface@5.0.0-alpha.4
|
|
30
|
-
|
|
31
|
-
## 5.0.0-alpha.3
|
|
32
|
-
|
|
33
|
-
### Patch Changes
|
|
34
|
-
|
|
35
|
-
- Updated dependencies []:
|
|
36
|
-
- @open-wa/driver-interface@5.0.0-alpha.3
|
|
37
|
-
|
|
38
|
-
## 5.0.0-alpha.2
|
|
39
|
-
|
|
40
|
-
### Patch Changes
|
|
41
|
-
|
|
42
|
-
- Updated dependencies []:
|
|
43
|
-
- @open-wa/driver-interface@5.0.0-alpha.2
|
|
44
|
-
|
|
45
|
-
## 6.0.0-alpha.2
|
|
46
|
-
|
|
47
|
-
### Minor Changes
|
|
48
|
-
|
|
49
|
-
- Dry run minor bump
|
|
50
|
-
|
|
51
|
-
### Patch Changes
|
|
52
|
-
|
|
53
|
-
- Updated dependencies []:
|
|
54
|
-
- @open-wa/driver-interface@6.0.0-alpha.2
|
|
55
|
-
|
|
56
|
-
## 5.0.1-alpha.2
|
|
57
|
-
|
|
58
|
-
### Patch Changes
|
|
59
|
-
|
|
60
|
-
- Dry run patch bump
|
|
61
|
-
|
|
62
|
-
- Updated dependencies []:
|
|
63
|
-
- @open-wa/driver-interface@5.0.1-alpha.2
|
package/src/PlaywrightBrowser.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { IBrowser, IPage, DriverCapabilities } from '@open-wa/driver-interface';
|
|
2
|
-
import type { Browser } from 'playwright';
|
|
3
|
-
import { PlaywrightPage } from './PlaywrightPage';
|
|
4
|
-
import { PlaywrightBrowserType } from './PlaywrightDriver';
|
|
5
|
-
|
|
6
|
-
export class PlaywrightBrowser implements IBrowser {
|
|
7
|
-
readonly name = 'playwright' as const;
|
|
8
|
-
|
|
9
|
-
constructor(
|
|
10
|
-
private browser: Browser,
|
|
11
|
-
private browserType: PlaywrightBrowserType,
|
|
12
|
-
private capabilities: DriverCapabilities
|
|
13
|
-
) {}
|
|
14
|
-
|
|
15
|
-
async newPage(): Promise<IPage> {
|
|
16
|
-
const page = await this.browser.newPage();
|
|
17
|
-
return new PlaywrightPage(page, this.browserType, this.capabilities);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async pages(): Promise<IPage[]> {
|
|
21
|
-
const contexts = this.browser.contexts();
|
|
22
|
-
const allPages: IPage[] = [];
|
|
23
|
-
|
|
24
|
-
for (const context of contexts) {
|
|
25
|
-
const pages = context.pages();
|
|
26
|
-
allPages.push(...pages.map(p => new PlaywrightPage(p, this.browserType, this.capabilities)));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return allPages;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async close(): Promise<void> {
|
|
33
|
-
await this.browser.close();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
isConnected(): boolean {
|
|
37
|
-
return this.browser.isConnected();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async versionString(): Promise<string> {
|
|
41
|
-
return this.browser.version();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
unwrap(): Browser {
|
|
45
|
-
return this.browser;
|
|
46
|
-
}
|
|
47
|
-
}
|
package/src/PlaywrightDriver.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { IDriver, IBrowser, LaunchOptions, ConnectOptions, IDriverContext, DriverCapabilities } from '@open-wa/driver-interface';
|
|
2
|
-
import { PlaywrightBrowser } from './PlaywrightBrowser';
|
|
3
|
-
|
|
4
|
-
export type PlaywrightBrowserType = 'chromium' | 'firefox' | 'webkit' | 'chrome' | 'msedge';
|
|
5
|
-
|
|
6
|
-
export interface PlaywrightLaunchOptions extends LaunchOptions {
|
|
7
|
-
browser?: PlaywrightBrowserType;
|
|
8
|
-
channel?: 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary';
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class PlaywrightDriver implements IDriver {
|
|
12
|
-
readonly name = 'playwright' as const;
|
|
13
|
-
version?: string;
|
|
14
|
-
readonly capabilities: DriverCapabilities;
|
|
15
|
-
|
|
16
|
-
private playwright: any;
|
|
17
|
-
private ctx?: IDriverContext;
|
|
18
|
-
private browserType: PlaywrightBrowserType;
|
|
19
|
-
|
|
20
|
-
constructor(browserType: PlaywrightBrowserType = 'chromium') {
|
|
21
|
-
this.browserType = browserType;
|
|
22
|
-
|
|
23
|
-
this.capabilities = {
|
|
24
|
-
cdp: browserType === 'chromium' || browserType === 'chrome' || browserType === 'msedge'
|
|
25
|
-
? { supported: true, notes: 'Chromium-based only' }
|
|
26
|
-
: { supported: false, reason: 'CDP only available in Chromium browsers' },
|
|
27
|
-
requestInterception: { supported: true },
|
|
28
|
-
serviceWorkerBypass: { supported: true, notes: 'Different API than Puppeteer' },
|
|
29
|
-
stealth: { supported: false, reason: 'No puppeteer-extra equivalent' },
|
|
30
|
-
pdf: browserType === 'chromium' || browserType === 'chrome' || browserType === 'msedge'
|
|
31
|
-
? { supported: true, notes: 'Chromium-based only' }
|
|
32
|
-
: { supported: false, reason: 'PDF generation only in Chromium' },
|
|
33
|
-
tracing: { supported: true },
|
|
34
|
-
persistentContext: { supported: true },
|
|
35
|
-
browserExtensions: { supported: false, reason: 'Playwright limitation' },
|
|
36
|
-
exposeBinding: { supported: true },
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async init(ctx?: IDriverContext): Promise<void> {
|
|
41
|
-
this.ctx = ctx;
|
|
42
|
-
|
|
43
|
-
this.playwright = await import('playwright');
|
|
44
|
-
this.version = this.playwright.version || 'unknown';
|
|
45
|
-
|
|
46
|
-
this.ctx?.logger?.info('PlaywrightDriver initialized', {
|
|
47
|
-
version: this.version,
|
|
48
|
-
browserType: this.browserType
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async launch(options?: PlaywrightLaunchOptions): Promise<IBrowser> {
|
|
53
|
-
if (!this.playwright) await this.init();
|
|
54
|
-
|
|
55
|
-
const browserType = (options as PlaywrightLaunchOptions)?.browser || this.browserType;
|
|
56
|
-
const channel = (options as PlaywrightLaunchOptions)?.channel;
|
|
57
|
-
|
|
58
|
-
let browserEngine: any;
|
|
59
|
-
if (browserType === 'chrome' || browserType === 'msedge') {
|
|
60
|
-
browserEngine = this.playwright.chromium;
|
|
61
|
-
} else if (browserType === 'firefox') {
|
|
62
|
-
browserEngine = this.playwright.firefox;
|
|
63
|
-
} else if (browserType === 'webkit') {
|
|
64
|
-
browserEngine = this.playwright.webkit;
|
|
65
|
-
} else {
|
|
66
|
-
browserEngine = this.playwright[browserType];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const launchOptions: any = {
|
|
70
|
-
headless: options?.headless ?? true,
|
|
71
|
-
executablePath: options?.executablePath,
|
|
72
|
-
args: options?.args,
|
|
73
|
-
timeout: options?.timeoutMs,
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
if (channel) {
|
|
77
|
-
launchOptions.channel = channel;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const browser = await browserEngine.launch(launchOptions);
|
|
81
|
-
|
|
82
|
-
return new PlaywrightBrowser(browser, browserType, this.capabilities);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async connect(options: ConnectOptions): Promise<IBrowser> {
|
|
86
|
-
if (!this.playwright) await this.init();
|
|
87
|
-
|
|
88
|
-
const browser = await this.playwright.chromium.connect({
|
|
89
|
-
wsEndpoint: options.wsEndpoint,
|
|
90
|
-
timeout: options.timeoutMs,
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return new PlaywrightBrowser(browser, this.browserType, this.capabilities);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
unwrap(): any {
|
|
97
|
-
return this.playwright;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
has<C extends string>(cap: C): boolean {
|
|
101
|
-
return (this.capabilities as any)[cap]?.supported === true;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
require<C extends string>(cap: C): void {
|
|
105
|
-
if (!this.has(cap)) {
|
|
106
|
-
const capability = (this.capabilities as any)[cap];
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Driver '${this.name}' does not support capability '${cap}'${
|
|
109
|
-
capability?.reason ? `: ${capability.reason}` : ''
|
|
110
|
-
}`
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { IElementHandle } from '@open-wa/driver-interface';
|
|
2
|
-
import type { ElementHandle } from 'playwright';
|
|
3
|
-
|
|
4
|
-
export class PlaywrightElementHandle implements IElementHandle {
|
|
5
|
-
constructor(private element: ElementHandle) {}
|
|
6
|
-
|
|
7
|
-
async click(): Promise<void> {
|
|
8
|
-
await this.element.click();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
async type(text: string): Promise<void> {
|
|
12
|
-
await this.element.type(text);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async getAttribute(name: string): Promise<string | null> {
|
|
16
|
-
const value = await this.element.getAttribute(name);
|
|
17
|
-
return value;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async textContent(): Promise<string | null> {
|
|
21
|
-
const text = await this.element.textContent();
|
|
22
|
-
return text;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async dispose(): Promise<void> {
|
|
26
|
-
await this.element.dispose();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
unwrap(): ElementHandle {
|
|
30
|
-
return this.element;
|
|
31
|
-
}
|
|
32
|
-
}
|
package/src/PlaywrightPage.ts
DELETED
|
@@ -1,412 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
IPage,
|
|
3
|
-
IElementHandle,
|
|
4
|
-
DriverCapabilities,
|
|
5
|
-
type DisposableHandle,
|
|
6
|
-
type IConsoleMessage,
|
|
7
|
-
type IFrame,
|
|
8
|
-
type IRequest,
|
|
9
|
-
type IRequestContinueOverrides,
|
|
10
|
-
type IRequestResponse,
|
|
11
|
-
type NavigationWaitUntil,
|
|
12
|
-
type PageEventHandler,
|
|
13
|
-
type WaitForFunctionOptions,
|
|
14
|
-
} from '@open-wa/driver-interface';
|
|
15
|
-
import type { ConsoleMessage, Frame, Page, Request, CDPSession } from 'playwright';
|
|
16
|
-
import { PlaywrightElementHandle } from './PlaywrightElementHandle';
|
|
17
|
-
import { PlaywrightBrowserType } from './PlaywrightDriver';
|
|
18
|
-
|
|
19
|
-
type ListenerWrapper = (...args: any[]) => void | Promise<void>;
|
|
20
|
-
|
|
21
|
-
const PLAYWRIGHT_REQUEST_ROUTE_ERROR = 'Playwright request event payload cannot abort, continue, or respond without a bound route';
|
|
22
|
-
|
|
23
|
-
class PlaywrightDisposableHandle implements DisposableHandle {
|
|
24
|
-
constructor(private readonly disposer: () => Promise<void> | void) {}
|
|
25
|
-
|
|
26
|
-
dispose(): Promise<void> | void {
|
|
27
|
-
return this.disposer();
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
class PlaywrightFrame implements IFrame {
|
|
32
|
-
constructor(private readonly frame: Frame, private readonly isMain: boolean) {}
|
|
33
|
-
|
|
34
|
-
url(): string {
|
|
35
|
-
return this.frame.url();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
name(): string {
|
|
39
|
-
return this.frame.name();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
isMainFrame(): boolean {
|
|
43
|
-
return this.isMain;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
parentFrame(): IFrame | null {
|
|
47
|
-
const parent = this.frame.parentFrame();
|
|
48
|
-
return parent ? new PlaywrightFrame(parent, false) : null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async content(): Promise<string> {
|
|
52
|
-
return this.frame.content();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
unwrap(): Frame {
|
|
56
|
-
return this.frame;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
class PlaywrightRequest implements IRequest {
|
|
61
|
-
constructor(private readonly request: Request, private readonly page: Page) {}
|
|
62
|
-
|
|
63
|
-
url(): string {
|
|
64
|
-
return this.request.url();
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
method(): string {
|
|
68
|
-
return this.request.method();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
headers(): Record<string, string> {
|
|
72
|
-
return this.request.headers();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
resourceType(): string | null {
|
|
76
|
-
return this.request.resourceType?.() ?? null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
isNavigationRequest(): boolean {
|
|
80
|
-
return this.request.isNavigationRequest();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
frame(): IFrame | null {
|
|
84
|
-
const frame = this.request.frame();
|
|
85
|
-
return frame ? new PlaywrightFrame(frame, frame === this.page.mainFrame()) : null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async abort(): Promise<void> {
|
|
89
|
-
throw new Error(PLAYWRIGHT_REQUEST_ROUTE_ERROR);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async continue(_overrides?: IRequestContinueOverrides): Promise<void> {
|
|
93
|
-
throw new Error(PLAYWRIGHT_REQUEST_ROUTE_ERROR);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async respond(_response: IRequestResponse): Promise<void> {
|
|
97
|
-
throw new Error(PLAYWRIGHT_REQUEST_ROUTE_ERROR);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
unwrap(): Request {
|
|
101
|
-
return this.request;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
class PlaywrightConsoleMessage implements IConsoleMessage {
|
|
106
|
-
constructor(private readonly message: ConsoleMessage) {}
|
|
107
|
-
|
|
108
|
-
type(): string {
|
|
109
|
-
return this.message.type();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
text(): string {
|
|
113
|
-
return this.message.text();
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
location(): { url?: string; lineNumber?: number; columnNumber?: number } {
|
|
117
|
-
const location = this.message.location();
|
|
118
|
-
return {
|
|
119
|
-
url: location.url,
|
|
120
|
-
lineNumber: location.lineNumber,
|
|
121
|
-
columnNumber: location.columnNumber,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
args(): unknown[] {
|
|
126
|
-
return this.message.args();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
unwrap(): ConsoleMessage {
|
|
130
|
-
return this.message;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
export class PlaywrightPage implements IPage {
|
|
135
|
-
readonly name = 'playwright' as const;
|
|
136
|
-
private readonly listenerWrappers = new Map<string, Map<Function, Set<ListenerWrapper>>>();
|
|
137
|
-
private readonly requestInterceptionRoute = async (route: { continue: () => Promise<void> }) => {
|
|
138
|
-
await route.continue();
|
|
139
|
-
};
|
|
140
|
-
private requestInterceptionEnabled = false;
|
|
141
|
-
|
|
142
|
-
constructor(
|
|
143
|
-
private page: Page,
|
|
144
|
-
_browserType: PlaywrightBrowserType,
|
|
145
|
-
private capabilities: DriverCapabilities
|
|
146
|
-
) { }
|
|
147
|
-
|
|
148
|
-
async goto(url: string, options?: { waitUntil?: NavigationWaitUntil; timeoutMs?: number }): Promise<void> {
|
|
149
|
-
const waitUntil = options?.waitUntil === 'networkidle' ? 'networkidle' : options?.waitUntil;
|
|
150
|
-
|
|
151
|
-
await this.page.goto(url, {
|
|
152
|
-
waitUntil: waitUntil as any,
|
|
153
|
-
timeout: options?.timeoutMs,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
url(): string {
|
|
158
|
-
return this.page.url();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async reload(): Promise<void> {
|
|
162
|
-
await this.page.reload();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
mainFrame(): IFrame {
|
|
166
|
-
return new PlaywrightFrame(this.page.mainFrame(), true);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async evaluate<Arg, Ret>(fn: (arg: Arg) => Ret | Promise<Ret>, arg: Arg): Promise<Ret> {
|
|
170
|
-
return await this.page.evaluate(fn as any, arg as any) as Ret;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async evaluateScript<Ret = unknown>(script: string): Promise<Ret> {
|
|
174
|
-
return await this.page.evaluate(script) as Ret;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
async addInitScript(script: string): Promise<DisposableHandle> {
|
|
178
|
-
await this.page.addInitScript(script);
|
|
179
|
-
return new PlaywrightDisposableHandle(() => undefined);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async setViewport(viewport: { width: number; height: number }): Promise<void> {
|
|
183
|
-
await this.page.setViewportSize(viewport);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
async setUserAgent(ua: string): Promise<void> {
|
|
187
|
-
await this.page.setExtraHTTPHeaders({ 'User-Agent': ua });
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
async setRequestInterception(enabled: boolean): Promise<void> {
|
|
191
|
-
this.require('requestInterception');
|
|
192
|
-
if (enabled === this.requestInterceptionEnabled) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (enabled) {
|
|
197
|
-
await this.page.route('**/*', this.requestInterceptionRoute as any);
|
|
198
|
-
this.requestInterceptionEnabled = true;
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
await this.page.unroute('**/*', this.requestInterceptionRoute as any);
|
|
203
|
-
this.requestInterceptionEnabled = false;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async waitForSelector(selector: string, options?: { timeoutMs?: number }): Promise<IElementHandle | null> {
|
|
207
|
-
const element = await this.page.waitForSelector(selector, { timeout: options?.timeoutMs });
|
|
208
|
-
return element ? new PlaywrightElementHandle(element) : null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async waitForFunction(script: string, options?: WaitForFunctionOptions): Promise<void>;
|
|
212
|
-
async waitForFunction<Arg>(fn: (arg: Arg) => boolean, arg: Arg, options?: WaitForFunctionOptions): Promise<void>;
|
|
213
|
-
async waitForFunction<Arg>(fnOrScript: string | ((arg: Arg) => boolean), argOrOptions?: Arg | WaitForFunctionOptions, maybeOptions?: WaitForFunctionOptions): Promise<void> {
|
|
214
|
-
if (typeof fnOrScript === 'string') {
|
|
215
|
-
const options = argOrOptions as WaitForFunctionOptions | undefined;
|
|
216
|
-
await this.page.waitForFunction(fnOrScript, undefined, {
|
|
217
|
-
timeout: options?.timeoutMs,
|
|
218
|
-
polling: options?.polling as any,
|
|
219
|
-
});
|
|
220
|
-
return;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
await this.page.waitForFunction(fnOrScript as any, argOrOptions as any, {
|
|
224
|
-
timeout: maybeOptions?.timeoutMs,
|
|
225
|
-
polling: maybeOptions?.polling as any,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async $(selector: string): Promise<IElementHandle | null> {
|
|
230
|
-
const element = await this.page.$(selector);
|
|
231
|
-
return element ? new PlaywrightElementHandle(element) : null;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async $$(selector: string): Promise<IElementHandle[]> {
|
|
235
|
-
const elements = await this.page.$$(selector);
|
|
236
|
-
return elements.map(el => new PlaywrightElementHandle(el));
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async click(selector: string): Promise<void> {
|
|
240
|
-
await this.page.click(selector);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
async type(selector: string, text: string, options?: { delayMs?: number }): Promise<void> {
|
|
244
|
-
await this.page.type(selector, text, { delay: options?.delayMs });
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
async screenshot(options?: { type?: 'png' | 'jpeg'; fullPage?: boolean }): Promise<Uint8Array> {
|
|
248
|
-
const buffer = await this.page.screenshot({
|
|
249
|
-
type: options?.type,
|
|
250
|
-
fullPage: options?.fullPage,
|
|
251
|
-
});
|
|
252
|
-
return new Uint8Array(buffer);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
async exposeFunction(name: string, fn: (...args: any[]) => any): Promise<void> {
|
|
256
|
-
await this.page.exposeFunction(name, fn);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
on<K extends string>(event: K, handler: ((...args: any[]) => void | Promise<void>) | PageEventHandler<any>): DisposableHandle {
|
|
260
|
-
const wrapped = (...args: any[]) => {
|
|
261
|
-
const payload = this.wrapEventPayload(event, args);
|
|
262
|
-
if (event === 'close') {
|
|
263
|
-
return (handler as () => void | Promise<void>)();
|
|
264
|
-
}
|
|
265
|
-
return (handler as (payload: unknown) => void | Promise<void>)(payload);
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
this.getWrapperSet(event, handler as Function).add(wrapped);
|
|
269
|
-
this.page.on(event as any, wrapped as any);
|
|
270
|
-
|
|
271
|
-
return new PlaywrightDisposableHandle(() => {
|
|
272
|
-
this.removeWrappedListener(event, handler as Function, wrapped);
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
off<K extends string>(event: K, handler: ((...args: any[]) => void | Promise<void>) | PageEventHandler<any>): void {
|
|
277
|
-
const wrappers = this.listenerWrappers.get(event)?.get(handler as Function);
|
|
278
|
-
if (!wrappers || wrappers.size === 0) {
|
|
279
|
-
this.page.off(event as any, handler as any);
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
for (const wrapped of wrappers) {
|
|
284
|
-
this.page.off(event as any, wrapped as any);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
this.listenerWrappers.get(event)?.delete(handler as Function);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
async close(): Promise<void> {
|
|
291
|
-
await this.page.close();
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
isClosed(): boolean {
|
|
295
|
-
return this.page.isClosed();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Capability-specific methods
|
|
299
|
-
has<C extends string>(cap: C): boolean {
|
|
300
|
-
return (this.capabilities as any)[cap]?.supported === true;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
require<C extends string>(cap: C): void {
|
|
304
|
-
if (!this.has(cap)) {
|
|
305
|
-
const capability = (this.capabilities as any)[cap];
|
|
306
|
-
throw new Error(
|
|
307
|
-
`Page does not support capability '${cap}'${capability?.reason ? `: ${capability.reason}` : ''
|
|
308
|
-
}`
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// CDP Support (Chromium-based only)
|
|
314
|
-
cdp(): CDPSession {
|
|
315
|
-
this.require('cdp');
|
|
316
|
-
const context = this.page.context();
|
|
317
|
-
return (context as any).newCDPSession(this.page) as CDPSession;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Service Worker Bypass
|
|
321
|
-
async setBypassServiceWorker(bypass: boolean): Promise<void> {
|
|
322
|
-
this.require('serviceWorkerBypass');
|
|
323
|
-
await this.page.context().route('**/*', (route) => {
|
|
324
|
-
if (bypass && route.request().serviceWorker()) {
|
|
325
|
-
route.abort();
|
|
326
|
-
} else {
|
|
327
|
-
route.continue();
|
|
328
|
-
}
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// PDF Generation (Chromium only)
|
|
333
|
-
async pdf(options?: { path?: string; format?: string }): Promise<Uint8Array> {
|
|
334
|
-
this.require('pdf');
|
|
335
|
-
const buffer = await this.page.pdf(options as any);
|
|
336
|
-
return new Uint8Array(buffer);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Tracing
|
|
340
|
-
async startTracing(options?: { path?: string; screenshots?: boolean }): Promise<void> {
|
|
341
|
-
this.require('tracing');
|
|
342
|
-
await this.page.context().tracing.start(options as any);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async stopTracing(): Promise<Uint8Array> {
|
|
346
|
-
this.require('tracing');
|
|
347
|
-
const tempPath = `/tmp/trace-${Date.now()}.zip`;
|
|
348
|
-
await this.page.context().tracing.stop({ path: tempPath });
|
|
349
|
-
|
|
350
|
-
const fs = await import('fs/promises');
|
|
351
|
-
const buffer = await fs.readFile(tempPath);
|
|
352
|
-
await fs.unlink(tempPath).catch(() => { });
|
|
353
|
-
|
|
354
|
-
return new Uint8Array(buffer);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
unwrap(): Page {
|
|
358
|
-
return this.page;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
private getWrapperSet(event: string, handler: Function): Set<ListenerWrapper> {
|
|
362
|
-
let wrappers = this.listenerWrappers.get(event);
|
|
363
|
-
if (!wrappers) {
|
|
364
|
-
wrappers = new Map();
|
|
365
|
-
this.listenerWrappers.set(event, wrappers);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
let handlerWrappers = wrappers.get(handler);
|
|
369
|
-
if (!handlerWrappers) {
|
|
370
|
-
handlerWrappers = new Set();
|
|
371
|
-
wrappers.set(handler, handlerWrappers);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return handlerWrappers;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
private removeWrappedListener(event: string, handler: Function, wrapped: ListenerWrapper): void {
|
|
378
|
-
this.page.off(event as any, wrapped as any);
|
|
379
|
-
|
|
380
|
-
const eventWrappers = this.listenerWrappers.get(event);
|
|
381
|
-
const handlerWrappers = eventWrappers?.get(handler);
|
|
382
|
-
if (!handlerWrappers) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
handlerWrappers.delete(wrapped);
|
|
387
|
-
if (handlerWrappers.size === 0) {
|
|
388
|
-
eventWrappers?.delete(handler);
|
|
389
|
-
}
|
|
390
|
-
if (eventWrappers && eventWrappers.size === 0) {
|
|
391
|
-
this.listenerWrappers.delete(event);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
private wrapEventPayload(event: string, args: any[]): unknown {
|
|
396
|
-
const [payload] = args;
|
|
397
|
-
switch (event) {
|
|
398
|
-
case 'request':
|
|
399
|
-
case 'requestfailed':
|
|
400
|
-
case 'requestfinished':
|
|
401
|
-
return new PlaywrightRequest(payload as Request, this.page);
|
|
402
|
-
case 'framenavigated':
|
|
403
|
-
return new PlaywrightFrame(payload as Frame, payload === this.page.mainFrame());
|
|
404
|
-
case 'console':
|
|
405
|
-
return new PlaywrightConsoleMessage(payload as ConsoleMessage);
|
|
406
|
-
case 'pageerror':
|
|
407
|
-
return payload as Error;
|
|
408
|
-
default:
|
|
409
|
-
return payload;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { PlaywrightPage } from '../PlaywrightPage';
|
|
3
|
-
|
|
4
|
-
type MockHandler = (...args: any[]) => void | Promise<void>;
|
|
5
|
-
|
|
6
|
-
class MockPlaywrightPage {
|
|
7
|
-
private readonly listeners = new Map<string, Set<MockHandler>>();
|
|
8
|
-
|
|
9
|
-
readonly mainFrameValue = {
|
|
10
|
-
url: () => 'https://example.test',
|
|
11
|
-
name: () => 'main',
|
|
12
|
-
parentFrame: () => null,
|
|
13
|
-
content: vi.fn(async () => '<html></html>'),
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
readonly route = vi.fn(async () => undefined);
|
|
17
|
-
readonly unroute = vi.fn(async () => undefined);
|
|
18
|
-
readonly addInitScript = vi.fn(async () => undefined);
|
|
19
|
-
readonly setViewportSize = vi.fn(async () => undefined);
|
|
20
|
-
readonly setExtraHTTPHeaders = vi.fn(async () => undefined);
|
|
21
|
-
readonly waitForSelector = vi.fn(async () => null);
|
|
22
|
-
readonly waitForFunction = vi.fn(async () => undefined);
|
|
23
|
-
readonly $ = vi.fn(async () => null);
|
|
24
|
-
readonly $$ = vi.fn(async () => []);
|
|
25
|
-
readonly click = vi.fn(async () => undefined);
|
|
26
|
-
readonly type = vi.fn(async () => undefined);
|
|
27
|
-
readonly screenshot = vi.fn(async () => Buffer.from([]));
|
|
28
|
-
readonly exposeFunction = vi.fn(async () => undefined);
|
|
29
|
-
readonly close = vi.fn(async () => undefined);
|
|
30
|
-
readonly goto = vi.fn(async () => undefined);
|
|
31
|
-
readonly reload = vi.fn(async () => undefined);
|
|
32
|
-
readonly evaluate = vi.fn(async () => undefined);
|
|
33
|
-
readonly url = vi.fn(() => 'about:blank');
|
|
34
|
-
readonly isClosed = vi.fn(() => false);
|
|
35
|
-
readonly context = vi.fn(() => ({
|
|
36
|
-
route: vi.fn(async () => undefined),
|
|
37
|
-
tracing: {
|
|
38
|
-
start: vi.fn(async () => undefined),
|
|
39
|
-
stop: vi.fn(async () => undefined),
|
|
40
|
-
},
|
|
41
|
-
newCDPSession: vi.fn(async () => ({})),
|
|
42
|
-
}));
|
|
43
|
-
|
|
44
|
-
mainFrame() {
|
|
45
|
-
return this.mainFrameValue;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
on(event: string, handler: MockHandler): void {
|
|
49
|
-
const handlers = this.listeners.get(event) ?? new Set<MockHandler>();
|
|
50
|
-
handlers.add(handler);
|
|
51
|
-
this.listeners.set(event, handlers);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
off(event: string, handler: MockHandler): void {
|
|
55
|
-
this.listeners.get(event)?.delete(handler);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async emit(event: string, payload?: unknown): Promise<void> {
|
|
59
|
-
const handlers = [...(this.listeners.get(event) ?? [])];
|
|
60
|
-
await Promise.all(handlers.map((handler) => Promise.resolve(handler(payload))));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const capabilities = {
|
|
65
|
-
cdp: { supported: true },
|
|
66
|
-
requestInterception: { supported: true },
|
|
67
|
-
serviceWorkerBypass: { supported: true },
|
|
68
|
-
stealth: { supported: false, reason: 'unsupported' },
|
|
69
|
-
pdf: { supported: true },
|
|
70
|
-
tracing: { supported: true },
|
|
71
|
-
persistentContext: { supported: true },
|
|
72
|
-
browserExtensions: { supported: false, reason: 'unsupported' },
|
|
73
|
-
exposeBinding: { supported: true },
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
describe('PlaywrightPage', () => {
|
|
77
|
-
it('makes request interception toggles idempotent with a stable route handler', async () => {
|
|
78
|
-
const rawPage = new MockPlaywrightPage();
|
|
79
|
-
const page = new PlaywrightPage(rawPage as any, 'chromium', capabilities as any);
|
|
80
|
-
|
|
81
|
-
await page.setRequestInterception(true);
|
|
82
|
-
await page.setRequestInterception(true);
|
|
83
|
-
await page.setRequestInterception(false);
|
|
84
|
-
await page.setRequestInterception(false);
|
|
85
|
-
|
|
86
|
-
expect(rawPage.route).toHaveBeenCalledTimes(1);
|
|
87
|
-
expect(rawPage.unroute).toHaveBeenCalledTimes(1);
|
|
88
|
-
expect(rawPage.route).toHaveBeenCalledWith('**/*', expect.any(Function));
|
|
89
|
-
const registeredRouteHandler = (rawPage.route.mock.calls[0] as any[] | undefined)?.[1];
|
|
90
|
-
expect(rawPage.unroute).toHaveBeenCalledWith('**/*', registeredRouteHandler);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('keeps duplicate listeners independently disposable', async () => {
|
|
94
|
-
const rawPage = new MockPlaywrightPage();
|
|
95
|
-
const page = new PlaywrightPage(rawPage as any, 'chromium', capabilities as any);
|
|
96
|
-
const handler = vi.fn();
|
|
97
|
-
|
|
98
|
-
const first = page.on('request', handler);
|
|
99
|
-
const second = page.on('request', handler);
|
|
100
|
-
|
|
101
|
-
const request = {
|
|
102
|
-
url: () => 'https://example.test',
|
|
103
|
-
method: () => 'GET',
|
|
104
|
-
headers: () => ({ accept: 'application/json' }),
|
|
105
|
-
resourceType: () => 'document',
|
|
106
|
-
isNavigationRequest: () => true,
|
|
107
|
-
frame: () => rawPage.mainFrameValue,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
await rawPage.emit('request', request);
|
|
111
|
-
expect(handler).toHaveBeenCalledTimes(2);
|
|
112
|
-
|
|
113
|
-
await first.dispose();
|
|
114
|
-
await rawPage.emit('request', request);
|
|
115
|
-
expect(handler).toHaveBeenCalledTimes(3);
|
|
116
|
-
|
|
117
|
-
await second.dispose();
|
|
118
|
-
await rawPage.emit('request', request);
|
|
119
|
-
expect(handler).toHaveBeenCalledTimes(3);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('throws explicit route-bound errors for request mutation methods', async () => {
|
|
123
|
-
const rawPage = new MockPlaywrightPage();
|
|
124
|
-
const page = new PlaywrightPage(rawPage as any, 'chromium', capabilities as any);
|
|
125
|
-
let wrappedRequest: any;
|
|
126
|
-
|
|
127
|
-
page.on('request', (request: any) => {
|
|
128
|
-
wrappedRequest = request;
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
await rawPage.emit('request', {
|
|
132
|
-
url: () => 'https://example.test/api',
|
|
133
|
-
method: () => 'POST',
|
|
134
|
-
headers: () => ({ 'content-type': 'application/json' }),
|
|
135
|
-
resourceType: () => 'xhr',
|
|
136
|
-
isNavigationRequest: () => false,
|
|
137
|
-
frame: () => rawPage.mainFrameValue,
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
await expect(wrappedRequest.abort()).rejects.toThrow(/without a bound route/);
|
|
141
|
-
await expect(wrappedRequest.continue({ method: 'GET' })).rejects.toThrow(/without a bound route/);
|
|
142
|
-
await expect(wrappedRequest.respond({ status: 200, body: 'ok' })).rejects.toThrow(/without a bound route/);
|
|
143
|
-
});
|
|
144
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { PlaywrightDriver } from './PlaywrightDriver';
|
|
2
|
-
export type { PlaywrightBrowserType, PlaywrightLaunchOptions } from './PlaywrightDriver';
|
|
3
|
-
export { PlaywrightBrowser } from './PlaywrightBrowser';
|
|
4
|
-
export { PlaywrightPage } from './PlaywrightPage';
|
|
5
|
-
export { PlaywrightElementHandle } from './PlaywrightElementHandle';
|