ckerr

#54950: test: keep running deferred cleanup and afterEach hooks when one throws or hangs

Merged
Created: Oct 14, 2026, 4:49:57 PM
Merged: Oct 15, 2026, 1:15:50 AM
4 comments
Target: main

Description of Change

This fixes two ways that one bad defer()-ed cleanup could break more than its own test.

A throwing cleanup skipped every later afterEach hook. vitest runs a test's afterEach hooks in order and stops at the first one that throws. It skips the rest, including every enclosing suite's afterEach hooks. The spec runner registers defer()-ed cleanup as the first afterEach hook of every suite, so one failing cleanup also skipped hooks such as protocol.unhandle() and closeWindow().

In the no-cors protocol specs on CI, a cleanup that failed with ENOTEMPTY caused two problems:

  • The no-cors handler stayed registered, so the next try failed in beforeEach with Failed to register protocol: no-cors.
  • A window was left open, which a later closeWindow() reported as a leak.

spec/vitest/runner.ts now catches what each afterEach hook throws, defer()-ed cleanup included, and lets the remaining hooks run. It rethrows the errors from an onTestFinished callback once all the hooks have finished. The test still fails, and vitest reports each error separately with its original stack. Hooks and cleanups run in the same order as before.

A cleanup that never settled would hang the whole spec file. defer()-ed cleanup has had no timeout since the move to vitest (#54264). The mocha shim registered it with vitest's hook timeout set to 0, which vitest treats as no timeout, and the runner from #54439 adds it to the hook list directly, without vitest's afterEach(). So a cleanup such as a closeWindow() whose window never emits closed would hang the worker until the CI job timed out. Each defer()-ed cleanup now gets the hook timeout, as an afterEach hook would. The timeout error points at the defer() call. A timed-out cleanup is handled like a throwing one, so the remaining cleanups and hooks still run; as with a timed-out vitest hook, it keeps running in the background.

The new spec/vitest-runner.spec.ts covers both cases in nested suites: cleanups and hooks that throw, and a cleanup that outlasts a short hook timeout set through vi.setConfig(). It checks that every later cleanup and hook still ran and that the test failed with the expected errors. It is a new spec file, so it adds one short Electron launch to the CI shard that runs it.

🤖 Generated with Claude Code

Checklist

Release Notes

Notes: none

Backports

43-x-y
Merged
PR Number
#54995
Merged At
Oct 15, 2026, 9:47:08 AM
Released In
Not yet
Release Date
Not yet
44-x-y
Merged
PR Number
#54994
Merged At
Oct 15, 2026, 8:59:24 AM
Released In
Not yet
Release Date
Not yet
45-x-y
Merged
PR Number
#54997
Merged At
Oct 15, 2026, 3:01:23 AM
Released In
Not yet
Release Date
Not yet

Semver Impact

Major
Breaking changes
Minor
New features
Patch
Bug fixes
None
Docs, tests, etc.

Semantic Versioning helps users understand the impact of updates:

  • Major (X.y.z): Breaking changes that may require code modifications
  • Minor (x.Y.z): New features that maintain backward compatibility
  • Patch (x.y.Z): Bug fixes that don't change the API
  • None: Changes that don't affect using facing parts of Electron