Lifecycle Hooks
The four hooks
Section titled “The four hooks”Hooks run code around your tests. There are two run-level hooks and two test-level hooks:
| Hook | Runs | Receives |
|---|---|---|
| beforeAll | Once, before any test in the run | runInfo |
| beforeEach | Before every test | orbit, testInfo |
| afterEach | After every test (including failed ones) | orbit, testInfo |
| afterAll | Once, after the whole run | runInfo |
const { beforeAll, afterAll, beforeEach, afterEach, test } = require("orbittest");
beforeAll(async (runInfo) => { console.log(`Run started: ${runInfo.runId}`);});
beforeEach(async (orbit, testInfo) => { console.log(`Starting ${testInfo.name}, attempt ${testInfo.attempt}`);});
afterEach(async (orbit, testInfo) => { if (testInfo.status === "failed") { await orbit.screenshot(`reports/${testInfo.name}.png`); }});
afterAll(async (runInfo) => { console.log(`Run finished: ${runInfo.status}`);});All four hooks in one file.
testInfo: knowing where you are
Section titled “testInfo: knowing where you are”Test-level hooks (and the test itself, as a second argument) receive testInfo, which describes the current test:
| Field | Meaning |
|---|---|
| name, file, index | Which test this is and where it lives. |
| attempt, retry, retries | Retry state: attempt 2 means the first try failed. |
| timeout | The effective time budget. |
| status | In afterEach: “passed” or “failed”. |
| startedAt, endedAt, durationMs | Timing. |
| error | The failure error, when there is one. |
| artifacts | Paths of screenshots and other evidence captured so far. |
The most common pattern is conditional cleanup or evidence in afterEach, like the failure screenshot above. Because afterEach runs even when the test failed, it is the right place for anything that must always happen.
Global setup: hooks for the whole suite
Section titled “Global setup: hooks for the whole suite”When every test file needs the same hooks, do not copy them around. Put them in one file and point the config at it:
module.exports = { globalSetup: "tests/setup.js"};const { beforeAll, afterEach } = require("orbittest");
beforeAll(async (runInfo) => { console.log(`Starting run ${runInfo.runId}`);});
afterEach(async (orbit, testInfo) => { if (testInfo.status === "failed") { await orbit.screenshot(`reports/${testInfo.name}.png`); }});One setup file shared by the whole suite.
Use hooks without hiding the story
Section titled “Use hooks without hiding the story”Hooks have a failure mode: they can make tests unreadable by moving the important parts off-screen. A reviewer opening a test should still be able to tell what user behavior it covers. Two rules keep you safe:
- Put technical preparation in hooks: opening the base URL, seeding data, clearing artifacts. These are not part of the user story.
- Keep user actions visible in the test, or in a clearly named helper called from the test (
await loginAs(orbit, "admin")is fine; an invisible auto-login in beforeEach that the test never mentions is not).
Also be careful with beforeAll state. Anything created once and shared across tests is a flakiness risk in parallel runs: two workers can race on the same record. If shared state causes intermittent failures, move the setup into beforeEach or into the tests themselves and measure the actual cost; it is often smaller than feared.
Frequently asked questions
Section titled “Frequently asked questions”Do hooks run for retried tests?
Section titled “Do hooks run for retried tests?”Yes. Each attempt is a full test run, so beforeEach and afterEach run again. Check testInfo.attempt if a hook should behave differently on retries.
Can a test file have hooks and tests together?
Section titled “Can a test file have hooks and tests together?”Yes. File-local hooks apply to that file. Suite-wide hooks belong in a globalSetup file.
What happens if afterEach itself throws?
Section titled “What happens if afterEach itself throws?”The error is reported against the test run. Keep cleanup code defensive: check that the thing you are cleaning actually exists first.
Related pages
Section titled “Related pages”- Test Basics — How to structure readable end-to-end tests: arrange-act-assert, the orbit object, per-test options, and strong assertions.
- Configuration — Every orbittest.config.js option explained: workers, retries, timeouts, browser display, CI behavior, and named environments.
- Browser Storage — Cookies, localStorage, sessionStorage, saved login sessions with saveSession/loadSession, and session health checks.
- Reports & Diagnostics — HTML, JSON, and JUnit reports, trace timelines, Smart Report browser evidence, step mode, and report cleanup.