ohos-playwright 0.2.8 → 0.2.10
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/README.md +41 -1
- package/dist/fixture.d.mts +12 -1
- package/dist/fixture.mjs +114 -4
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -31,10 +31,50 @@ export default defineConfig(withOpenHarmony({ /* your config */ }))
|
|
|
31
31
|
{ "scripts": { "test:e2e": "ohos-playwright test" } }
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Supported APIs
|
|
35
|
+
|
|
36
|
+
The following Playwright APIs have been validated on ArkWeb / HarmonyOS 6.1 (Chromium 132):
|
|
37
|
+
|
|
38
|
+
| Category | APIs |
|
|
39
|
+
|---|---|
|
|
40
|
+
| Network interception | `page.route()`, `route.fulfill()`, `route.abort()`, `page.unroute()` |
|
|
41
|
+
| Screenshot | `page.screenshot({ type: 'jpeg' \| 'png' })`, `locator.screenshot()` |
|
|
42
|
+
| Geolocation | `context.setGeolocation()`, `context.grantPermissions(['geolocation'])` |
|
|
43
|
+
| Device emulation | `emulateDevice` fixture (see below) |
|
|
44
|
+
| Input | `locator.fill()`, `locator.type()`, `keyboard.press()` |
|
|
45
|
+
| Cookies | `context.addCookies()`, `context.cookies()`, `context.clearCookies()` |
|
|
46
|
+
| Dialog | `page.on('dialog')`, `dialog.accept()`, `dialog.dismiss()`, `dialog.message()`, `dialog.type()` |
|
|
47
|
+
| Popup | `context.waitForEvent('page')` + `window.open()` — stub Page with `url()`, `waitForLoadState()`, `close()` |
|
|
48
|
+
| Page events | `page.on('pageerror')`, `page.on('console')`, `page.on('download')` |
|
|
49
|
+
| Frames | `page.frames()`, `page.mainFrame()`, `frame.url()` |
|
|
50
|
+
| Viewport | `page.viewportSize()` (pre-fetched via `Page.getLayoutMetrics` for reused CDP tabs) |
|
|
51
|
+
| Media emulation | `page.emulateMedia({ colorScheme })` |
|
|
52
|
+
|
|
53
|
+
### `emulateDevice` fixture
|
|
54
|
+
|
|
55
|
+
Because `newContext()` is not supported in connectOverCDP mode, device emulation is exposed as a Playwright fixture parameter backed by CDP `Emulation.*` commands.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { test, expect } from '@playwright/test'
|
|
59
|
+
import type { DeviceDescriptor } from 'ohos-playwright/fixture'
|
|
60
|
+
|
|
61
|
+
test('mobile viewport', async ({ page, emulateDevice }) => {
|
|
62
|
+
await emulateDevice({
|
|
63
|
+
viewport: { width: 375, height: 812 },
|
|
64
|
+
deviceScaleFactor: 3,
|
|
65
|
+
isMobile: true,
|
|
66
|
+
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) ...',
|
|
67
|
+
})
|
|
68
|
+
expect(await page.evaluate(() => window.innerWidth)).toBe(375)
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`emulateDevice` settings persist for the lifetime of the page. Call it again with `{ viewport: { width: 1280, height: 720 }, isMobile: false }` to restore defaults.
|
|
73
|
+
|
|
34
74
|
## Limitations
|
|
35
75
|
|
|
36
76
|
- **Chromium only.** firefox and webkit aren't available on HarmonyOS.
|
|
37
|
-
- **One context, one page.** `newContext()` / `newPage()` aren't supported. Isolate tests with `localStorage.clear()` + `page.reload()`.
|
|
77
|
+
- **One context, one page.** `newContext()` / `newPage()` aren't supported. Isolate tests with `localStorage.clear()` + `page.reload()`. For device emulation use the `emulateDevice` fixture instead of `browser.newContext({ ...device })`.
|
|
38
78
|
- **`process.platform` reads `'linux'`** during the run — we patch it because Playwright's hostPlatform detection only branches on linux/darwin/win32 and falls through to `<unknown>` on openharmony. For real platform checks use `process.env.OHOS_PW_HOST`.
|
|
39
79
|
|
|
40
80
|
## Environment variables
|
package/dist/fixture.d.mts
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface DeviceDescriptor {
|
|
2
|
+
viewport: {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
};
|
|
6
|
+
deviceScaleFactor?: number;
|
|
7
|
+
isMobile?: boolean;
|
|
8
|
+
userAgent?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & {
|
|
11
|
+
emulateDevice: (descriptor: DeviceDescriptor) => Promise<void>;
|
|
12
|
+
}, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
2
13
|
export { expect } from '@playwright/test';
|
package/dist/fixture.mjs
CHANGED
|
@@ -12,22 +12,132 @@ export const test = base.extend({
|
|
|
12
12
|
},
|
|
13
13
|
{ scope: 'worker' },
|
|
14
14
|
],
|
|
15
|
-
context: async ({ browser }, use) => {
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
context: async ({ browser }, use, testInfo) => {
|
|
16
|
+
const ctx = browser.contexts()[0];
|
|
17
|
+
// Inject baseURL into the context's private _options so that internal
|
|
18
|
+
// Playwright URL resolution (toHaveURL, waitForURL, locators) works for
|
|
19
|
+
// SPA-navigated pages — page.goto patching alone is not sufficient.
|
|
20
|
+
const baseURL = testInfo.project.use.baseURL;
|
|
21
|
+
if (baseURL) {
|
|
22
|
+
;
|
|
23
|
+
ctx._options.baseURL = baseURL;
|
|
24
|
+
}
|
|
25
|
+
await use(ctx);
|
|
18
26
|
},
|
|
19
27
|
page: async ({ context }, use, testInfo) => {
|
|
20
28
|
const pages = context.pages();
|
|
21
29
|
if (pages.length === 0)
|
|
22
30
|
throw new Error('No pages in ArkWeb CDP context. Open a tab first.');
|
|
23
31
|
const page = pages.find((p) => p.url().startsWith('http://localhost')) ?? pages[0];
|
|
32
|
+
const ctxEmit = context.emit.bind(context);
|
|
33
|
+
// Patch baseURL
|
|
24
34
|
const baseURL = testInfo.project.use.baseURL;
|
|
25
35
|
if (baseURL) {
|
|
26
36
|
const root = baseURL.replace(/\/+$/, '');
|
|
27
37
|
const origGoto = page.goto.bind(page);
|
|
28
38
|
page.goto = ((url, opts) => origGoto((url.startsWith('/') && !url.startsWith('//')) ? root + url : url, opts));
|
|
29
39
|
}
|
|
30
|
-
|
|
40
|
+
// connectOverCDP reuses an existing tab — Playwright has no record of its
|
|
41
|
+
// viewport size and viewportSize() returns null. Pre-fetch via CDP.
|
|
42
|
+
const session = await context.newCDPSession(page);
|
|
43
|
+
try {
|
|
44
|
+
const { cssVisualViewport } = await session.send('Page.getLayoutMetrics');
|
|
45
|
+
const cached = {
|
|
46
|
+
width: Math.round(cssVisualViewport.clientWidth),
|
|
47
|
+
height: Math.round(cssVisualViewport.clientHeight),
|
|
48
|
+
};
|
|
49
|
+
const origViewportSize = page.viewportSize.bind(page);
|
|
50
|
+
page.viewportSize = () => origViewportSize() ?? cached;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Non-critical — viewportSize() will still return null if CDP call fails.
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
await session.detach();
|
|
57
|
+
}
|
|
58
|
+
// ArkWeb's new tab from window.open() is invisible to CDP (Target.createTarget hangs,
|
|
59
|
+
// Target.targetCreated never fires). Intercept via an init script:
|
|
60
|
+
// - queue the URL for our poller
|
|
61
|
+
// - return null (Window object hangs CDP serialization if returned)
|
|
62
|
+
// Guard against multiple addInitScript calls across tests accumulating overrides.
|
|
63
|
+
const origEvaluate = page.evaluate.bind(page);
|
|
64
|
+
const alreadyPatched = page['__ohosPopupPatched'];
|
|
65
|
+
if (!alreadyPatched) {
|
|
66
|
+
;
|
|
67
|
+
page['__ohosPopupPatched'] = true;
|
|
68
|
+
await page.addInitScript(() => {
|
|
69
|
+
if (window['__ohosPopupPatched'])
|
|
70
|
+
return;
|
|
71
|
+
window['__ohosPopupPatched'] = true;
|
|
72
|
+
window['__ohosPopupQueue'] = [];
|
|
73
|
+
window.open = (url) => {
|
|
74
|
+
;
|
|
75
|
+
window['__ohosPopupQueue'].push({ url: String(url ?? '') });
|
|
76
|
+
return null; // Window object hangs CDP serialization — return null instead
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const popupPoller = setInterval(async () => {
|
|
81
|
+
try {
|
|
82
|
+
const pending = await origEvaluate(() => {
|
|
83
|
+
const q = window['__ohosPopupQueue'];
|
|
84
|
+
window['__ohosPopupQueue'] = [];
|
|
85
|
+
return q;
|
|
86
|
+
});
|
|
87
|
+
for (const { url } of pending ?? []) {
|
|
88
|
+
// context.newPage() calls Target.createTarget which hangs in ArkWeb.
|
|
89
|
+
// Emit a minimal stub — satisfies waitForLoadState / url / close.
|
|
90
|
+
const stub = {
|
|
91
|
+
waitForLoadState: async () => { },
|
|
92
|
+
url: () => url,
|
|
93
|
+
close: async () => { },
|
|
94
|
+
};
|
|
95
|
+
ctxEmit('page', stub);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch { }
|
|
99
|
+
}, 150);
|
|
100
|
+
// evaluate() exceptions reject the promise but never become pageerror events
|
|
101
|
+
// (CDP catches them before they become uncaught). Intercept and re-emit.
|
|
102
|
+
// Save and restore to prevent wrapper accumulation across tests on the same page object.
|
|
103
|
+
const savedEvaluate = page['evaluate'];
|
|
104
|
+
page.evaluate = async (fn, arg) => {
|
|
105
|
+
try {
|
|
106
|
+
return await origEvaluate(fn, arg);
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
110
|
+
page.emit('pageerror', err);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
await use(page);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
clearInterval(popupPoller);
|
|
118
|
+
page.evaluate = savedEvaluate;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
emulateDevice: async ({ page }, use) => {
|
|
122
|
+
await use(async (descriptor) => {
|
|
123
|
+
const session = await page.context().newCDPSession(page);
|
|
124
|
+
try {
|
|
125
|
+
await session.send('Emulation.setDeviceMetricsOverride', {
|
|
126
|
+
width: descriptor.viewport.width,
|
|
127
|
+
height: descriptor.viewport.height,
|
|
128
|
+
deviceScaleFactor: descriptor.deviceScaleFactor ?? 1,
|
|
129
|
+
mobile: descriptor.isMobile ?? false,
|
|
130
|
+
});
|
|
131
|
+
if (descriptor.userAgent !== undefined) {
|
|
132
|
+
await session.send('Emulation.setUserAgentOverride', {
|
|
133
|
+
userAgent: descriptor.userAgent,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
await session.detach();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
31
141
|
},
|
|
32
142
|
});
|
|
33
143
|
export { expect } from '@playwright/test';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ohos-playwright",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "Playwright adapter for OpenHarmony / ArkWeb via hdc + CDP",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "social4hyq",
|
|
@@ -25,13 +25,14 @@
|
|
|
25
25
|
"type": "module",
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc && node scripts/fix-extensions.mjs",
|
|
28
|
-
"test": "node --test src/*.test.mts",
|
|
28
|
+
"test": "node --test $(ls src/*.test.mts | grep -v api-coverage)",
|
|
29
29
|
"typecheck": "tsc --noEmit"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=24"
|
|
33
33
|
},
|
|
34
34
|
"exports": {
|
|
35
|
+
".": "./dist/fixture.mjs",
|
|
35
36
|
"./fixture": "./dist/fixture.mjs",
|
|
36
37
|
"./setup": "./dist/setup.mjs",
|
|
37
38
|
"./teardown": "./dist/teardown.mjs",
|