Skip to content

Add test.cleanup() and test.cleanupEach() for reliable cleanup tasks#3469

Open
stevenmini2019 wants to merge 1 commit into
avajs:mainfrom
stevenmini2019:cleanup-hooks
Open

Add test.cleanup() and test.cleanupEach() for reliable cleanup tasks#3469
stevenmini2019 wants to merge 1 commit into
avajs:mainfrom
stevenmini2019:cleanup-hooks

Conversation

@stevenmini2019

@stevenmini2019 stevenmini2019 commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Implements reliable cleanup tasks for #928. Adds two new chain methods:

  • test.cleanup(fn) — registers fn as both a before hook and an after.always hook. The after.always half always runs, even when a test fails or --fail-fast aborts the run, so idempotent cleanup code can rely on it to remove state left behind by the current (or a previously crashed) run.
  • test.cleanupEach(fn) — registers fn as both a beforeEach hook and an afterEach.always hook, so it runs around every test.

Both are also available on test.serial (test.serial.cleanup, test.serial.cleanupEach) and support .skip().

Why this design

The root problem in #928 is that after/.always hooks are not guaranteed to run after a failure or when a process exits/crashes mid-run, leaving stale fixtures behind. By pairing a before (or beforeEach) registration with an after.always (or afterEach.always) registration, the cleanup code is guaranteed to run on the way out — this is the contract test.cleanup() and test.cleanupEach() provide.

Implementation

  • lib/create-chain.js — adds a createCleanupChain helper that wires cleanup/cleanupEach into both root and root.serial, with .skip() support.
  • lib/runner.js — the chain-metadata callback now recognizes the cleanup/cleanupEach task types and pushes the implementation into two task arrays: before + afterAlways (for cleanup) and beforeEach + afterEachAlways (for cleanupEach). The after halves are flagged always: true so they run even on failure.
  • types/test-fn.d.ts — adds a CleanupFn type and exposes cleanup/cleanupEach on TestFn and SerialFn.
  • test-types/module/conditional-chains.ts — type-level coverage for the new chains.
  • test/create-chain/test.js — metadata-level assertions (runs in CI).
  • test/cleanup/test.js — runtime integration test proving the dual execution, including the on-failure guarantee.
  • docs/01-writing-tests.md — documents test.cleanup() / test.cleanupEach().

Verification

  • xo passes on all modified files.
  • test/cleanup/test.js passes locally: cleanup runs as before + after.always, cleanupEach runs around every test, and a failing test still triggers the after.always half (the core guarantee of this feature).

Notes / open question

Unlike the previously-open PR #3457 (which only touched create-chain.js and never connected the runtime), this wires the behavior all the way through runner.js so the hooks actually execute. Happy to adjust the API surface (e.g. method names) if maintainers prefer a different convention.


IssueHunt Summary

Referenced issues

This pull request has been submitted to:


Implements avajs#928. `test.cleanup(fn)` registers fn as both a `before` hook and an
`after.always` hook; `test.cleanupEach(fn)` registers it as both a `beforeEach`
hook and an `afterEach.always` hook. The "after" halves always run, even when a test
fails or --fail-fast is used, so idempotent cleanup code can rely on them to remove
state left behind by the current (or a previously crashed) run.

Both are available on test.serial and support .skip().
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