ci speed: desktop boot check runs its cases in parallel (one browser, killed at the end; a page per case; condition waits instead of fixed sleeps: element, text or a quiet window of host calls and DOM); shots capture / render close the browser with a 3 s cap, then kill it; console engine waits injectable for tests (WAITS / engineWaits: poll rounds, a missing step ends once its loaded page stays quiet, team switch settle, field-miss grace), fillPage looks up each data type's dialog once per round and skips dialog sections when no dialog is open; browser tests run 4 at a time per file (testScope().concurrent with a scope per test, --max-concurrency in ci), 20 ms rounds; capture-web counts boots per test and runs its browser tests concurrently; console-session / -persist Chrome tests concurrent; recorded test timings smoothed (mergeTimings) so one loaded run does not skew the shard split
This commit is contained in:
parent
f2d7062bcb
commit
9ba8471002
16 changed files with 390 additions and 178 deletions
|
|
@ -13,6 +13,8 @@
|
|||
// - polling sleeps in the waits for log lines: event-driven now (resolved on each new line)
|
||||
// Hook timeouts are bounded by the numbers below (each close is capped), never open-ended.
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { AsyncLocalStorage } from 'node:async_hooks';
|
||||
import { test } from 'bun:test';
|
||||
|
||||
/** Launching Chromium: well above a cold start on a loaded machine (seconds), far below "hung". */
|
||||
export const LAUNCH_TIMEOUT_MS = 60_000;
|
||||
|
|
@ -25,6 +27,16 @@ export const CLOSE_TIMEOUT_MS = 15_000;
|
|||
export const AFTER_EACH_MS = 2 * CLOSE_TIMEOUT_MS + 5_000;
|
||||
export const AFTER_ALL_MS = CLOSE_TIMEOUT_MS + 5_000;
|
||||
|
||||
/**
|
||||
* The console engine's timings for tests on local fixture pages (production: a 1 s watch round, 200 ms poll rounds,
|
||||
* a missing step waited out for the whole step timeout). A round of 20 ms; a poll ends as "not found" once the page
|
||||
* is loaded and unchanged for 1.5 s (well above the one timer a fixture page has, asc-shell.html's 700 ms redirect),
|
||||
* so a step that is not there no longer costs the whole navTimeoutMs, while a step that is there is still waited for
|
||||
* as long as the machine needs. Set with engineWaits (cli/console/fill.ts) in beforeAll, restored in afterAll.
|
||||
*/
|
||||
export const TEST_TICK_MS = 20;
|
||||
export const TEST_WAITS = { pollMs: 50, quietMs: 1500, settleMs: 50, missMs: 300 };
|
||||
|
||||
/** Waits for `p` (its error ignored) or `ms`, whichever is first: true when `p` settled in time. Never rejects. */
|
||||
export async function capped(p: Promise<unknown>, ms: number): Promise<boolean> {
|
||||
let t: ReturnType<typeof setTimeout> | undefined;
|
||||
|
|
@ -88,11 +100,8 @@ export async function persistentBrowser(): Promise<{ label: string | null; skipW
|
|||
: { label: null, skipWhy: 'neither Google Chrome nor Playwright\'s full Chromium is installed (the headless shell drops session cookies on relaunch); install Google Chrome or run `bunx playwright install chromium`' };
|
||||
}
|
||||
|
||||
/**
|
||||
* The contexts and engine runs of the current test. `newPage` opens a page in a fresh context (as browser.newPage
|
||||
* does) and tracks it; `track` adds one opened elsewhere; `settle` (afterEach) closes them all and waits for the runs.
|
||||
*/
|
||||
export function testScope(browser: () => any) {
|
||||
/** The contexts and engine runs of one test (see testScope). */
|
||||
function plainScope(browser: () => any) {
|
||||
const contexts = new Set<any>();
|
||||
const runs = new Set<Promise<unknown>>();
|
||||
return {
|
||||
|
|
@ -113,6 +122,33 @@ export function testScope(browser: () => any) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The contexts and engine runs of the current test. `newPage` opens a page in a fresh context (as browser.newPage
|
||||
* does) and tracks it; `track` adds one opened elsewhere; `settle` (afterEach) closes them all and waits for the runs.
|
||||
* `concurrent` declares a browser test that runs at the same time as the file's other concurrent tests
|
||||
* (test.concurrent, at most --max-concurrency at once: tools/ci.ts passes BROWSER_CONCURRENCY from tools/ci-plan.ts): it gets a scope of
|
||||
* its own (AsyncLocalStorage, so newPage / track / run inside it reach that scope), settled when it ends, inside its
|
||||
* timeout. The tests share the one browser; each has its own contexts, pages and log.
|
||||
*/
|
||||
export function testScope(browser: () => any) {
|
||||
type Scope = ReturnType<typeof plainScope>;
|
||||
const shared = plainScope(browser);
|
||||
const own = new AsyncLocalStorage<Scope>();
|
||||
const cur = (): Scope => own.getStore() ?? shared;
|
||||
return {
|
||||
newPage: (opts?: Record<string, unknown>): Promise<any> => cur().newPage(opts),
|
||||
track: <T>(ctx: T): T => cur().track(ctx),
|
||||
run: <T>(p: Promise<T>): Promise<T> => cur().run(p),
|
||||
settle: (): Promise<void> => shared.settle(),
|
||||
concurrent(name: string, fn: () => Promise<void>, ms: number): void {
|
||||
test.concurrent(name, () => {
|
||||
const s = plainScope(browser);
|
||||
return own.run(s, async () => { try { await fn(); } finally { await s.settle(); } });
|
||||
}, ms + AFTER_EACH_MS);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The log lines of an engine run (`say`), with event-driven waits: `wait` resolves on the line that makes `pred`
|
||||
* true (checked on every new line, no polling) and fails after `ms` with every line seen so far.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue