@monoes/monobrowse 1.0.15 → 1.0.17
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/dist/src/__tests__/launch-close-settle.test.d.ts +32 -0
- package/dist/src/__tests__/launch-close-settle.test.d.ts.map +1 -0
- package/dist/src/__tests__/launch-close-settle.test.js +155 -0
- package/dist/src/__tests__/launch-close-settle.test.js.map +1 -0
- package/dist/src/__tests__/report-exit-code.test.d.ts +46 -0
- package/dist/src/__tests__/report-exit-code.test.d.ts.map +1 -0
- package/dist/src/__tests__/report-exit-code.test.js +188 -0
- package/dist/src/__tests__/report-exit-code.test.js.map +1 -0
- package/dist/src/browser/actions.d.ts.map +1 -1
- package/dist/src/browser/actions.js +19 -9
- package/dist/src/browser/actions.js.map +1 -1
- package/dist/src/browser/bridge.d.ts.map +1 -1
- package/dist/src/browser/bridge.js +4 -1
- package/dist/src/browser/bridge.js.map +1 -1
- package/dist/src/browser/browser.d.ts.map +1 -1
- package/dist/src/browser/browser.js +62 -7
- package/dist/src/browser/browser.js.map +1 -1
- package/dist/src/browser/cdp.d.ts.map +1 -1
- package/dist/src/browser/cdp.js +8 -1
- package/dist/src/browser/cdp.js.map +1 -1
- package/dist/src/cli/commands-report.d.ts.map +1 -1
- package/dist/src/cli/commands-report.js +46 -20
- package/dist/src/cli/commands-report.js.map +1 -1
- package/dist/src/report/util.d.ts +4 -0
- package/dist/src/report/util.d.ts.map +1 -1
- package/dist/src/report/util.js +9 -3
- package/dist/src/report/util.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/__tests__/launch-close-settle.test.ts +175 -0
- package/src/__tests__/report-exit-code.test.ts +207 -0
- package/src/browser/actions.ts +21 -12
- package/src/browser/bridge.ts +4 -1
- package/src/browser/browser.ts +63 -7
- package/src/browser/cdp.ts +8 -1
- package/src/cli/commands-report.ts +46 -19
- package/src/report/util.ts +9 -3
|
@@ -186,6 +186,34 @@ export const reportCommand: Command = {
|
|
|
186
186
|
trendWindow: ctx.flags['trend-window'] as number | undefined,
|
|
187
187
|
};
|
|
188
188
|
|
|
189
|
+
// Closing the browser is cleanup, not part of the verdict. It used to run
|
|
190
|
+
// in a `finally` sitting between "we have the result" and "we print it",
|
|
191
|
+
// which meant anything that wedged the teardown — a Chrome that never
|
|
192
|
+
// acknowledged `Browser.close`, a CDP socket that died silently — took the
|
|
193
|
+
// verdict down with it: no output at all, and exit 0 on a page that FAILED
|
|
194
|
+
// its budgets. That is exactly the signal CI, agents and `&&` chains gate
|
|
195
|
+
// on, so a hung teardown reported success on a broken page. The verdict is
|
|
196
|
+
// now printed and the exit code fixed BEFORE any of this runs.
|
|
197
|
+
const teardown = async (): Promise<void> => {
|
|
198
|
+
// A report is a one-shot command — CI should not be left with an
|
|
199
|
+
// orphan Chrome. Only close a browser THIS process launched: an
|
|
200
|
+
// attached one belongs to the user's own `open`/`connect` session.
|
|
201
|
+
if (ctx.flags['keep-open'] || browser.getLaunchedPid(session.port) === undefined) return;
|
|
202
|
+
browser.stopRequestCapture(sessionId);
|
|
203
|
+
browser.teardownConsoleCapture(sessionId);
|
|
204
|
+
try {
|
|
205
|
+
await browser.closeBrowser(client, session.port);
|
|
206
|
+
} catch {
|
|
207
|
+
/* best-effort */
|
|
208
|
+
}
|
|
209
|
+
session.client = null;
|
|
210
|
+
session.sessionId = '';
|
|
211
|
+
session.targetId = '';
|
|
212
|
+
session.refs = new Map();
|
|
213
|
+
await browser.clearActivePort();
|
|
214
|
+
await browser.clearRefCache();
|
|
215
|
+
};
|
|
216
|
+
|
|
189
217
|
let result:
|
|
190
218
|
| Awaited<ReturnType<typeof runReport>>
|
|
191
219
|
| Awaited<ReturnType<typeof runReportRepeated>>;
|
|
@@ -193,25 +221,11 @@ export const reportCommand: Command = {
|
|
|
193
221
|
result = repeat
|
|
194
222
|
? await runReportRepeated(client, sessionId, { ...runOptions, repeat })
|
|
195
223
|
: await runReport(client, sessionId, runOptions);
|
|
196
|
-
}
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
browser.stopRequestCapture(sessionId);
|
|
202
|
-
browser.teardownConsoleCapture(sessionId);
|
|
203
|
-
try {
|
|
204
|
-
await browser.closeBrowser(client, session.port);
|
|
205
|
-
} catch {
|
|
206
|
-
/* best-effort */
|
|
207
|
-
}
|
|
208
|
-
session.client = null;
|
|
209
|
-
session.sessionId = '';
|
|
210
|
-
session.targetId = '';
|
|
211
|
-
session.refs = new Map();
|
|
212
|
-
await browser.clearActivePort();
|
|
213
|
-
await browser.clearRefCache();
|
|
214
|
-
}
|
|
224
|
+
} catch (err) {
|
|
225
|
+
// No verdict to print on this path, so the old ordering still applies:
|
|
226
|
+
// clean up, then let the error propagate to the CLI's error handler.
|
|
227
|
+
await teardown();
|
|
228
|
+
throw err;
|
|
215
229
|
}
|
|
216
230
|
|
|
217
231
|
const { report, htmlPath, jsonPath, summary, historyDir } = result;
|
|
@@ -220,6 +234,15 @@ export const reportCommand: Command = {
|
|
|
220
234
|
// runs must not exit 0 just because the last run happened to be green.
|
|
221
235
|
const passed = flake ? flake.verdict === 'pass' : report.verdict === 'pass';
|
|
222
236
|
|
|
237
|
+
// Set the process-wide failure code up front, before printing and before
|
|
238
|
+
// teardown. Returning `exitCode` below is the normal channel, but it only
|
|
239
|
+
// reaches the CLI if this function returns; if teardown never settles,
|
|
240
|
+
// Node drains the event loop and exits on its own — and a natural exit
|
|
241
|
+
// uses process.exitCode. Setting it here means a failing page cannot exit
|
|
242
|
+
// 0 no matter what happens after this line. Only ever set on failure, so
|
|
243
|
+
// this never clears a non-zero code set elsewhere.
|
|
244
|
+
if (!passed) process.exitCode = 1;
|
|
245
|
+
|
|
223
246
|
if (ctx.flags.json) {
|
|
224
247
|
const { toJsonReport } = await import('../report/index.js');
|
|
225
248
|
print(
|
|
@@ -265,6 +288,10 @@ export const reportCommand: Command = {
|
|
|
265
288
|
if (historyDir) print(`History: ${historyDir}`);
|
|
266
289
|
}
|
|
267
290
|
|
|
291
|
+
// Verdict is printed and the exit code is fixed — only now is it safe to
|
|
292
|
+
// close the browser.
|
|
293
|
+
await teardown();
|
|
294
|
+
|
|
268
295
|
// Non-zero exit is the point of RIG-06 — CI and agents gate on it.
|
|
269
296
|
return {
|
|
270
297
|
success: passed,
|
package/src/report/util.ts
CHANGED
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
/** Budget for one non-navigation collector (AX tree, screenshot, eval). */
|
|
4
4
|
export const STEP_TIMEOUT_MS = 15_000;
|
|
5
5
|
|
|
6
|
+
/** Not unref'd: callers `await sleep(...)` (e.g. the web-vitals settle
|
|
7
|
+
* window), so this timer is on an actively-awaited path. An unref'd timer
|
|
8
|
+
* does not keep the event loop alive, so once the CDP socket closes Node
|
|
9
|
+
* would drain and exit mid-report instead of resuming after the sleep. */
|
|
6
10
|
export function sleep(ms: number): Promise<void> {
|
|
7
11
|
return new Promise((r) => {
|
|
8
|
-
|
|
9
|
-
t.unref?.();
|
|
12
|
+
setTimeout(r, ms);
|
|
10
13
|
});
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -23,9 +26,12 @@ export async function withTimeout<T>(promise: Promise<T>, ms: number, label: str
|
|
|
23
26
|
try {
|
|
24
27
|
return await Promise.race([
|
|
25
28
|
promise,
|
|
29
|
+
// Not unref'd: this timer is the only thing that settles the race when
|
|
30
|
+
// `promise` is waiting on a socket that quietly went away, and every
|
|
31
|
+
// caller awaits the result. The finally below clears it, so work that
|
|
32
|
+
// finishes in time never holds the event loop open.
|
|
26
33
|
new Promise<never>((_, reject) => {
|
|
27
34
|
timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
|
28
|
-
timer.unref?.();
|
|
29
35
|
}),
|
|
30
36
|
]);
|
|
31
37
|
} finally {
|