DavertMik · GitHub

and others added 2 commits

June 14, 2026 11:37
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 )
…tryTo 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 )

@DavertMik

@DavertMik

DavertMik deleted the reland/asyncwrapper-and-core-fixes branch

June 14, 2026 12:28

Read the original on github.com ↗