Skip to content

fix(core): re-land asyncWrapper + promise-core error-path fixes onto 4.x (#5624, #5633)#5637

Merged
DavertMik merged 2 commits into
4.xfrom
reland/asyncwrapper-and-core-fixes
Jun 14, 2026
Merged

fix(core): re-land asyncWrapper + promise-core error-path fixes onto 4.x (#5624, #5633)#5637
DavertMik merged 2 commits into
4.xfrom
reland/asyncwrapper-and-core-fixes

Conversation

@DavertMik

Copy link
Copy Markdown
Contributor

Why this PR exists

#5624 and #5633 show as merged, but their changes never reached 4.x. Both were stacked on the advisor/001-promise-core-characterization branch (the base of #5622). #5622 was squash-merged to 4.x first; #5624 and #5633 then merged into the advisor/001 branch afterward. So their commits live on advisor/001 and are not on 4.x.

Verified on current 4.x (20607b70): asyncWrapper.js has no .catch hardening, and session.js/effects.js have none of the error-path fixes.

This PR re-lands both deltas directly on 4.x (cherry-picked; no stacking this time).

What's included

1. fix(mocha) — fail fast when test hook import chain rejects (was #5624)

  • setup()/teardown() in lib/mocha/asyncWrapper.js now .catch(err => doneFn(err)) on the dynamic import('./test.js') chain (mirrors suiteSetup/suiteTeardown), so a rejected import reports an error instead of hanging the hook forever.
  • 3 regression tests; reverting the fix makes the error-path tests hang.

2. fix(core) — restore sessions & propagate errors on session/within/retryTo error paths (was #5633)

  • within() async error now runs _withinEnd (finishHelpers()) and return recorder.promise() (error propagates, not detached).
  • session() async/sync error now restores the recorder session (fixes the session-id leak) — surgical: only adds recorder.session.restore, keeping the original restoreVars call so BROWSER_RESTART=session doesn't close the browser context (this was the regression that failed Playwright CI on fix(core): restore sessions and propagate errors on session/within/retryTo error paths #5633 and was fixed before merge).
  • retryTo(): a thrown callback routes through recorder.throw (no premature reject) so throw-then-succeed resolves and reject happens only after maxTries; first attempt is tries === 1 (was 2), retry count preserved.
  • Characterization assertions in session_composition_test.js updated to the fixed behavior.

Verification (cherry-picked onto current 4.x)

  • npx mocha target unit files → 44 passing
  • BROWSER_RESTART=session acceptance (session_test.js) → 9 passed, 2 skipped, 0 browser context has been closed
  • lint + prettier clean

Both changes were already CI-green on #5624/#5633 (incl. the full playwright.yml suite across browsers + restart modes); CI will re-run here against current 4.x.

After merge

The advisor/001, advisor/003, advisor/006 branches can be deleted — their content is now fully represented on 4.x (via #5622 + this PR).

🤖 Generated with Claude Code

DavertMik 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 merged commit eba4a62 into 4.x Jun 14, 2026
13 checks passed
@DavertMik DavertMik deleted the reland/asyncwrapper-and-core-fixes branch June 14, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant