Commits on Jun 13, 2026
-
test(core): characterize promise-core error and hang paths (#5622)
Adds browser-free characterization tests that pin down the error and settle-guarantees of the promise composition core, so it can be fixed or refactored safely. No lib/ code is changed — current behavior (including known-bad behavior) is frozen. - recorder_test.js: errHandler/catch routing, catchWithoutStop terminal vs normal errors, ignoreErr, nested-session id semantics, unbalanced session restore, and task timeout (success + failure). - session_composition_test.js (new): session()/within()/retryTo()/hopeThat() composition with a fake helper registered through the real container, a settles()/drain() harness that turns deadlocks into fast named failures, and hermetic per-test isolation of the recorder singleton. - mocha/asyncWrapper_test.js: test() lifecycle (queued-step failure, sync throw, test.throws pass) and a rejecting injected() before-hook. Characterized divergences (session-id leak on error, within skipping _withinEnd, retryTo retrying past rejection, recorder.retries leaking across runs, stopped-recorder hang) are documented for the follow-up fix plan. Co-authored-by: DavertMik <davert@testomat.io> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
3 people authored
Jun 13, 2026
Commits on Jun 14, 2026
-
fix(pause): remove leaked dispatcher listeners and guard session rest…
…ore (#5630) Every pause() call registered two permanent listeners on the global event dispatcher (step.after, test.finished) and never removed them. Repeated pauses — now the normal case because the MCP server drives pause() programmatically via setPauseHandler/pauseNow — accumulated listeners, fired finish() multiple times, and ran an unconditional recorder.session.restore('pause') on every test finish even when no pause session was open, unbalancing the recorder's session stack (the hang class blocking 4.0). - Convert the two anonymous listeners into named handlers (onStepAfter, onTestFinished) and register them through an idempotent helper that removes any prior registration first, so repeated pause()/pauseNow() keep exactly one of each. onTestFinished removes both listeners when the test finishes. - Track an open-pause flag and only restore the 'pause' session when one is actually open (set on session.start in pauseSession, cleared at all three restore sites). - pauseNow now performs the same idempotent registration as pause(). setPauseHandler/pauseNow signatures and resolve semantics are unchanged (bin/mcp-server.js untouched). 4 regression tests cover idempotent registration, listener removal on finish, the no-double-restore guard, and the MCP pauseNow lifecycle; reverting the fix fails the listener tests. Co-authored-by: DavertMik <davert@testomat.io> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
3 people authored
Jun 14, 2026 -
fix(core): re-land asyncWrapper + promise-core error-path fixes onto …
…4.x (#5624, #5633) (#5637) * fix(mocha): fail fast when test hook import chain rejects In 4.x the per-test setup()/teardown() hooks load ./test.js via a dynamic import() with no .catch(). If that import rejects, or the .then body throws (e.g. enhanceMochaTest on an undefined test, a listener throwing), done() is never called and the mocha hook hangs forever — the silent-hang failure mode the 4.0 release is most concerned with. This mirrors the existing suiteSetup()/suiteTeardown() shape exactly: append a .catch(err => doneFn(err)) to both import chains. No recorder.errHandler is added (the per-test handler owns the single errFn slot). makeDoneCallableOnce already guards against a double done() call. Adds 3 regression tests: setup() and teardown() with a throwing then-body call done with the error within 1s instead of hanging, plus a happy-path check. Reverting the fix makes the two error-path tests hang (verified). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit ac59a40 ) * fix(core): restore sessions and propagate errors on session/within/retryTo error paths Fixes four of the latent error-path divergences characterized in #5622, by making the error paths symmetric with the success paths. The characterization assertions are updated to the corrected behavior in the same commit. - within() async error (lib/effects.js): the catch now calls finishHelpers() (so helpers' _withinEnd runs on error, not just success) and returns recorder.promise() (so the error propagates through within() instead of being detached onto a trailing task that a caller awaiting within() never sees). - session() async error (lib/session.js): schedules a recorder task that runs recorder.session.restore so the recorder session is restored on error (it was only restored on success, leaking the session id). The existing restoreVars/listener cleanup is kept as-is — switching to finalize()'s real restoreVars() closes the browser context under BROWSER_RESTART=session. - session() sync error (lib/session.js): the finally recorder.catch now calls recorder.session.restore before re-throwing (the only place that runs on a rejected chain). - retryTo() (lib/effects.js): a thrown callback no longer reject()s the outer promise prematurely — it routes through recorder.throw so the retry logic owns the outcome (retry, or reject once maxTries is exhausted). A callback that throws then succeeds on a later attempt now resolves instead of rejecting. tries now starts at 1 on the first attempt (was 2); the retry count is preserved (tries < maxTries). Verified: unit 748/0, runner 273/0 (incl. all retryFailedStep/rerun tests), acceptance within/session/els green under both BROWSER_RESTART=browser and =session. Not changed here (would be unsafe or out of scope, see PR): recorder.retries clearing (retryFailedStep depends on it), the stopped-recorder no-op contract, and nested cross-level restore ordering. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit dca7626 ) --------- Co-authored-by: DavertMik <davert@testomat.io> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
3 people authored
Jun 14, 2026
Commits on Jun 17, 2026
-
fix(typescript): unique temp file names to fix run-multiple race (#5642…
…) (#5643) Transpiled TypeScript files were written to a fixed "<source>.temp.mjs" path next to the source. Under run-multiple, every forked worker transpiles the same files to the same temp paths and cleans them up independently, so one worker's cleanup deletes files the others still need to import — surfacing as "Cannot find module *.temp.mjs". Include process.pid plus a random suffix in the temp file name so each worker writes (and removes) its own files. The names still end in ".temp.mjs", so stack-trace remapping and fixErrorStack keep working. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>