@ai-ecoverse/slicc-shared-web 1.4.0 → 1.4.1
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 +3 -2
- package/harness/page.mjs +29 -8
- package/harness/recorder.mjs +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,12 +63,13 @@ test('boots', async (t) => {
|
|
|
63
63
|
| `args` | Extra Chromium flags, such as `--host-resolver-rules` or `--ignore-certificate-errors` | none |
|
|
64
64
|
| `timeout` | How long `page.until` polls, in ms | `30000` |
|
|
65
65
|
|
|
66
|
-
`chrome.page(t)` opens a tab and returns the page API: `goto(path)`, `reload()`, `evaluate(fn, ...args)`, `until(fn, ...args)`, `press(key, ...modifiers)` (a key name like `Enter` or `F5`, or a single character typed with its US-layout key code), `type(text)`, `insert(text)`, `enter()`, `init(fn)` (runs in every new document), `expose(name, handler)` (a binding the page calls with a JSON string), `screenshot(file)`, `tab()` (another tab in the same context), `close()`, `errors`, `responses` and `dir` (the test's artifact directory). `chrome.requests` lists the paths the server saw in this test, `chrome.overrides` is a `Map` of paths to bodies served instead of files, and `chrome.cdn` takes `status` (answer every intercepted request with it), `corrupt` (flip the last byte) and lists the intercepted `requests`. All of them reset with each test. `serve(options)` starts the same server on its own, for an `npm start`.
|
|
66
|
+
`chrome.page(t)` opens a tab and returns the page API: `goto(path)`, `reload()`, `evaluate(fn, ...args)`, `until(fn, ...args)`, `press(key, ...modifiers)` (a key name like `Enter` or `F5`, or a single character typed with its US-layout key code), `type(text)`, `insert(text)`, `enter()`, `init(fn)` (runs in every new document), `expose(name, handler)` (a binding the page calls with a JSON string), `screenshot(file)`, `tab()` (another tab in the same context), `close()`, `errors`, `responses` and `dir` (the test's artifact directory). `goto` and `reload` wait until the new document is complete and no longer `about:blank`, and retry the navigation once if that never happens. `until` polls until the function returns a truthy value (not only strict `true`) and resolves with that value; the timeout error names the last result. `chrome.requests` lists the paths the server saw in this test, `chrome.overrides` is a `Map` of paths to bodies served instead of files, and `chrome.cdn` takes `status` (answer every intercepted request with it), `corrupt` (flip the last byte) and lists the intercepted `requests`. All of them reset with each test. `serve(options)` starts the same server on its own, for an `npm start`.
|
|
67
67
|
|
|
68
68
|
Why it works the way it does:
|
|
69
69
|
|
|
70
70
|
- Playwright detaches from shared workers, which resumes them early, so their coverage started late about half the time. Raw CDP keeps every worker paused until the profiler runs.
|
|
71
|
-
- `debugger;` pauses in `beforeunload` but not in `pagehide` or `pageswap`, so a `beforeunload` hook, installed after `Page.enable`, takes the coverage of a page before it navigates. The pause only answers CDP when the page itself starts the navigation, so `goto` and `reload` go through `location`, never `Page.navigate` or `Page.reload`.
|
|
71
|
+
- `debugger;` pauses in `beforeunload` but not in `pagehide` or `pageswap`, so a `beforeunload` hook, installed after `Page.enable`, takes the coverage of a page before it navigates. The pause only answers CDP when the page itself starts the navigation, so `goto` and `reload` go through `location`, never `Page.navigate` or `Page.reload`. Navigating away from `about:blank` skips the coverage dump so the first real load is not held in `beforeunload` for a useless snapshot.
|
|
72
|
+
- `goto` used to return as soon as the document swapped (`window.stale` cleared), which is before `readyState` is `complete` and before module scripts run. About 1 load in 20 then looked blank to the next assertion. It now waits for a complete, non-blank document.
|
|
72
73
|
- `takePreciseCoverage` resets the counters, so every snapshot is a delta, and all of them are kept and summed.
|
|
73
74
|
- monocart's `add()` from parallel test processes loses data, so each test writes raw JSON and the teardown merges it once.
|
|
74
75
|
- Coverage of a script served from `chrome.overrides` would be mapped onto the file on disk, so a path overridden during a test is left out of that test's coverage.
|
package/harness/page.mjs
CHANGED
|
@@ -73,7 +73,15 @@ export function mark() {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
export function fresh() {
|
|
76
|
-
return !window.stale;
|
|
76
|
+
return !window.stale && document.readyState === 'complete' && location.href !== 'about:blank';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const specials = { Infinity, '-Infinity': -Infinity, NaN, '-0': -0 };
|
|
80
|
+
|
|
81
|
+
export function decoded({ value, unserializableValue }) {
|
|
82
|
+
if (unserializableValue === undefined) return value;
|
|
83
|
+
if (unserializableValue.endsWith('n')) return BigInt(unserializableValue.slice(0, -1));
|
|
84
|
+
return specials[unserializableValue];
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
export function page(cdp, { sessionId, targetId }, server, timeout = 30000) {
|
|
@@ -99,18 +107,24 @@ export function page(cdp, { sessionId, targetId }, server, timeout = 30000) {
|
|
|
99
107
|
returnByValue: true,
|
|
100
108
|
});
|
|
101
109
|
if (exceptionDetails) throw new Error(exceptionDetails.exception?.description);
|
|
102
|
-
return result
|
|
110
|
+
return decoded(result);
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
async function until(fn, ...args) {
|
|
106
114
|
const deadline = Date.now() + timeout;
|
|
107
115
|
let last;
|
|
108
116
|
while (Date.now() < deadline) {
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
try {
|
|
118
|
+
last = await evaluate(fn, ...args);
|
|
119
|
+
if (last) return last;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
last = error.message;
|
|
122
|
+
}
|
|
111
123
|
await sleep(25);
|
|
112
124
|
}
|
|
113
|
-
throw new Error(
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Timed out waiting for a truthy result from ${fn}\nlast result: ${JSON.stringify(last)}`
|
|
127
|
+
);
|
|
114
128
|
}
|
|
115
129
|
|
|
116
130
|
async function press(key, ...modifiers) {
|
|
@@ -120,9 +134,16 @@ export function page(cdp, { sessionId, targetId }, server, timeout = 30000) {
|
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
async function navigate(expression) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
138
|
+
await evaluate(mark);
|
|
139
|
+
await send('Runtime.evaluate', { expression });
|
|
140
|
+
try {
|
|
141
|
+
await until(fresh);
|
|
142
|
+
return;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (attempt === 1) throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
126
147
|
}
|
|
127
148
|
|
|
128
149
|
return {
|
package/harness/recorder.mjs
CHANGED
|
@@ -154,7 +154,12 @@ export function recorder(cdp, server, { coverage = ['/'], exits = new Map() } =
|
|
|
154
154
|
async function paused(sessionId) {
|
|
155
155
|
const target = sessions.get(sessionId);
|
|
156
156
|
if (target?.type === 'worker') await dump(sessionId, target, false);
|
|
157
|
-
else
|
|
157
|
+
else {
|
|
158
|
+
const { result } = await cdp
|
|
159
|
+
.send('Runtime.evaluate', { expression: 'location.href', returnByValue: true }, sessionId)
|
|
160
|
+
.catch(() => ({ result: {} }));
|
|
161
|
+
if (result?.value && result.value !== 'about:blank') await checkpoint();
|
|
162
|
+
}
|
|
158
163
|
await cdp.send('Debugger.resume', {}, sessionId).catch(ignore);
|
|
159
164
|
}
|
|
160
165
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-shared-web",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Shared Renovate, Biome, TypeScript, lefthook and GitHub Actions configuration, plus the CDP integration-test harness, for SLICC's web repos",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|