mochajs/mocha
github.com/mochajs/mocha ↗ · 664 files · 1.8 MB
☕️ Classic, reliable, trusted test framework for Node.js and the browser ☕️ Links - Documentation - Release Notes / History / Changes - Code of Conduct - Contributing - Development - Discord (ask questions here!) - Issue Tracker Backers Become a backer and sho
Overview
Mocha 12.0.3 is a JavaScript test framework for developers running tests through a Node.js CLI, a programmatic API, or a browser. It builds suites from interchangeable test interfaces, executes synchronous, callback-based, and Promise-returning tests, and exposes results through event-driven reporters. Its parallel mode runs test files in child processes and reconstructs buffered results for the main reporter; its documentation is a separate Astro/Starlight application.
System map
The package has distinct CLI, library, and browser entrypoints sharing one suite and runner model. Node-specific loading and parallel execution live behind separate modules, while browser bundling excludes those facilities. Runtime state is primarily held in memory, with optional report files as output artifacts. Documentation and release automation have separate build and external-service boundaries.
- package.json declares an ESM package, index.js as its library entrypoint, bin/mocha.js as the mocha executable, and Node ^20.19.0 or >=22.12.0.
- Mocha owns options, input files, a root Suite, lifecycle state, and the runner class; its constructor binds the interface and reporter before execution.
- The CLI separates option discovery, command dispatch, run preparation, and file collection into options.cjs, cli.js, run.cjs, and collect-files.cjs.
- ParallelBufferedRunner delegates files to BufferedWorkerPool, whose workerType is explicitly process and whose children inherit process.execArgv.
- Rollup builds browser-entry.js into a UMD mocha.js with a source map; package browser mappings exclude CLI, filesystem, worker, and serializer modules.
- Suite stores test structure and inherited context in memory; JSON and XUnit reporters provide explicit filesystem output boundaries rather than a database-backed state layer.
- The separate docs package builds Astro/Starlight, generates supporter data from Open Collective, and publishes docs/dist through Netlify configuration.
- GitHub Actions partitions runtime validation into smoke, node suites, browser tests, and tsc; Release Please conditionally publishes npm releases with provenance.
Feature catalog
Mocha exposes test execution and browser scaffolding through its CLI, plus direct construction and execution through its library API. Test declarations support multiple interfaces, hooks, selection, skipping, and retries. Reporters offer terminal, browser, structured, and CI-oriented output. Parallel and watch modes alter execution scheduling and lifecycle management.
- The CLI supports default test execution, init <path>, help, version, and interface/reporter listing; init writes a browser bundle, stylesheet, HTML runner, and empty test file.
- Built-in interfaces are BDD, TDD, QUnit, and exports; BDD provides describe/context, it/specify, four hook functions, and only/skip variants.
- Runnable supports synchronous functions, callbacks, and thenables; callback completion checks for a returned Promise once its result is available, and repeated completion emits a multiple-done error.
- Selection and run-policy options include grep/fgrep/invert, retries, bail, dry-run, fail-zero, forbid-only, forbid-pending, global leak checking, and forced exit.
- The reporter registry includes Spec, Dot, List, Min, TAP, JSON, JSON Stream, XUnit, HTML, Doc, Markdown, Progress, Landing, Nyan, and GitHub Actions, with normalized name aliases.
- Parallel mode supports jobs-based file concurrency but rejects --file, --sort, and progress/markdown/json-stream reporters; workers force forbidOnly.
- Watch mode supports file and ignore patterns, automatic reruns, the rs keyboard shortcut, and global teardown on SIGINT.
- Users can supply required-module root hook/global fixture plugins, custom reporters and interfaces, or programmatically load files and run; browser setup additionally accepts query-based grep, fgrep, and invert.
Data flow
Inputs enter through CLI arguments, environment options, config files, required modules, or browser scripts. Node file loading converts executable declarations into a Suite/Test graph before the runner executes it. Runner events feed statistics and reporters, while parallel mode transports serialized events across process boundaries. Persistent results are optional report artifacts whose paths come from reporter options.
- loadOptions merges CLI, MOCHA_OPTIONS, rc configuration, package.json's mocha property, and defaults in that precedence order, then combines spec and positional arguments and deduplicates them.
- prepareRunOptions loads --require modules through requireOrImport, discovers their plugin exports, finalizes plugin options, and merges them into argv after validating reporter and interface selection.
- collectFiles expands each spec through lookupFiles, filters ignored paths with minimatch, resolves absolute paths, sorts only spec files when requested, and prepends --file paths.
- loadFilesAsync processes files sequentially, surrounding requireOrImport with suite pre-require, require, and post-require events; BDD listeners turn declarations into Test objects attached to the active suite.
- Suite.create establishes parent/context inheritance, while addSuite propagates timeout, retries, slow threshold, and bail settings into child suites.
- Runner emits test outcomes and lifecycle events; createStatsCollector mutates runner.stats with counts and timestamps, and JSONReporter converts tests and errors into a results object.
- The worker pool serializes options, invokes worker run over IPC, and deserializes the returned result; worker run serializes buffered results, which the parent re-emits as reporter events.
- JSON output uses recursive directory creation and synchronous writes with stdout fallback; browser tests instead expose failures and counts through window.__mochaResults for Playwright to read.
Behavior traces
The source supports concrete traces through CLI startup, serial execution, parallel execution, browser execution, and report persistence. Startup can branch into a new Node process when runtime flags are present. Serial execution loads declarations centrally, while parallel execution defers loading to workers. Completion travels through runner events, reporter completion, and the CLI exit handler.
- CLI startup: bin/mocha.js calls loadOptions → partitions Node/Mocha options → disables timeouts for applicable debugger flags → either spawn(process.execPath, args) or main([], mochaArgs).
- Default command preparation: main → runCommand → default spec ['test'] → normalizeRunOptions → validateRunOptions → await prepareRunOptions → handler → new Mocha(argv) → runMocha.
- Serial loading: runMocha selects singleRun → collectFiles → assigns mocha.files → awaits loadFilesAsync → emits pre-require → executes the module → emits require/post-require → calls mocha.run.
- Serial execution: Mocha.run creates runner/stats/reporter → optionally runs global setup → Runner.run filters only declarations → runSuite executes beforeAll → runTests executes beforeEach/test/afterEach → traverses children → afterAll → run end.
- Failure/completion branch: a retry clones and requeues the test before afterEach; exhausted retries call fail. Mocha then attempts global teardown, adds one failure if teardown throws, and invokes reporter.done or the callback before CLI exit handling.
- Parallel execution: parallelRun collects files without loading them → Mocha uses ParallelBufferedRunner → creates the pool → queues files → worker bootstrap loads requires/UI once → new Mocha(opts).addFile(filepath) → loadFilesAsync → run → serialize → parent replays events and terminates the pool.
- Browser execution: browser-entry creates an HTML-reporter Mocha instance → setup calls ui → ui emits pre-require into global scope → script declarations build suites → run applies URL filters → delegates to Mocha.run → highlights code when the DOM target exists.
- Report persistence: JSONReporter accumulates pass/fail/pending/end events → on run end cleans objects → sets runner.testResults → JSON.stringify → mkdirSync/writeFileSync when output is configured, otherwise stdout; write failure logs an error and falls back to stdout.
Testing signals
Tests and CI were inspected, not executed; no passing result or measured coverage is asserted. Mocha tests its Node implementation using its own CLI, with Unexpected, Sinon, Chai, and Node assertions visible in inspected tests. Playwright drives a Chromium browser harness. Coverage configuration and script selection impose explicit limits, so this bounded inspection does not establish exhaustive coverage.
- The inspected npm scripts and CI define checks, but this investigation executed none of them; their existence establishes intended validation only.
- Node tests use Mocha's own bin/mocha.js under nyc; .mocharc.yml loads test/setup.cjs, which installs an Unexpected clone with Sinon, EventEmitter, and repository assertion extensions.
- Dedicated scripts partition interfaces, JS API, exclusivity, reporters, required compilers, and unit/node-unit tests; the integration script selects only test/integration/**/*.spec.cjs despite .spec.js integration files in the inventory.
- Runner unit tests directly construct Suite/Runner/Test objects and assert grep totals and inversion; diff protection tests exercise large buffers, deep objects, many keys, and small-object output with timing assertions.
- Parallel integration tests assert imported it bindings, worker IDs, circular errors, retries, crashing-worker file attribution, and non-writable getters; serializer tests assert null prototypes and __proto__ removal.
- Global fixture integration tests cover setup/teardown execution, shared context, sequential functions, setup/teardown exceptions, and watch lifecycle; JSON reporter tests stub writes and verify stdout fallback and browser rejection.
- Playwright config runs Chromium with CI retries and traces; its four configured suites are BDD, TDD, QUnit, and ESM, each asserting zero failures and a positive test count. The inspected configuration does not run Firefox or WebKit.
- CI includes smoke on multiple Node versions and node suites on Ubuntu/Windows, with optional Codecov uploads; nyc excludes docs, repro, browser helpers, and scripts, while smoke explicitly does not verify every published file.
Risk map
These are source observations and explicitly labeled hypotheses, not confirmed vulnerabilities. Mocha intentionally executes local configuration, plugins, and tests with process privileges. Sensitive boundaries include worker option evaluation, child-process environment inheritance, caller-selected output paths, and process-level exception handling. Browser support and documentation generation also have validation and external-service limits.
- Configuration discovery ascends from cwd, and JavaScript rc files execute through require; --require and custom reporter loading also execute resolved user modules. These paths require trusted local inputs.
- Parallel options are serialized with unsafe:true and ignoreFunction:true, then evaluated by worker run; worker creation copies process.env and adds MOCHA_WORKER_ID.
- init and reporter output use caller-selected paths without a repository-root confinement check in the inspected functions; init writes fixed filenames and JSON output overwrites its destination.
- The executable can spawn Node with inherited stdio, remap /dev/fd descriptors, and forward termination signals; worker completion removes all uncaughtException and unhandledRejection listeners in its process.
- Buffered parallel reporting can lose a crashing worker's earlier passing events before flush; an integration test explicitly expects that loss while checking attribution to the crashing spec file.
- Module loading branches on process.features.require_module and has error-specific require/import fallbacks; watch rebuilding manipulates the require cache and rebinds interfaces, making module-cache behavior a sensitive compatibility area.
- Hypothesis: the browser test server should remain a development-only service because it serves repository files, binds without an explicit host, and its containment check is lexical rather than a realpath/symlink check; no exploit was validated. (inferred)
- Docs generation fetches remote avatar URLs with a timeout and rejects HTML responses, while GraphQL pagination has no explicit timeout in the inspected request; production Netlify builds can fail the supporter-download success threshold.
Extension points
The strongest extension contracts are interfaces, reporters, and required-module plugins. CLI option metadata centralizes names, types, aliases, and help text. Runner events are the shared observation API across serial and parallel modes. Some worker and loader customization hooks are internal and should be treated with that narrower stability expectation.
- Mocha.ui accepts an interface function, a built-in registry name, or a resolvable module, unwraps a default export, and invokes the binding function with the root suite.
- Interfaces attach to Suite's pre-require event; createCommon supplies suite factories and hook registration, allowing a new DSL to reuse shared only/skip behavior.
- Mocha.reporter accepts a constructor, built-in name, package ID, or cwd-relative module, unwraps default exports, and supplies reporterOption/reporterOptions aliases.
- Reporter constructors receive runner and options; Mocha invokes an optional reporter.done(failures, callback), while createStatsCollector makes runner.stats available before reporter construction.
- Required modules can export mochaHooks, mochaGlobalSetup, and mochaGlobalTeardown; hook factories may be asynchronous, and finalization merges four hook lists into rootHooks.
- Internal PluginLoader.create accepts pluginDefs and ignore lists; register enforces unique export names, and each definition can provide validate and async finalize functions.
- run-option-metadata.cjs centralizes option types, aliases, and descriptions; getRunOptionDefinitions feeds CLI help, and flag/type lookup functions derive from the same tables.
- loadFilesAsync accepts an esmDecorator; parallel reporters can request partial-object linking, and ParallelBufferedRunner exposes workerReporter for selecting an alternate worker reporter.
Contributor handoff
This brief is pinned to the inspected commit and provides a bounded source map. Tests and CI were inspected, not executed; future changes need authorized validation against the exact scripts. Follow the executable package and workflow configuration when onboarding prose differs. Preserve generated-output boundaries, public return contracts, and the distinction between main-process and worker lifecycles.
- Read AGENTS.md and the affected package/workflow first; its playbook calls for minimal changes, compatibility preservation, and trusting executable script configuration when prose disagrees.
- Tests and CI were inspected, not executed in this investigation; this bounded inventory is not proof of passing checks or exhaustive coverage, and smoke itself documents a package-content limitation.
- For CLI edits, read bin/mocha.js, cli.js, options.cjs, run.cjs, and run-helpers.cjs in execution order; required modules must load before legacy reporter/interface validation.
- For runner edits, preserve Mocha.run's immediate Runner return value and callback completion contract, lifecycle state guards, and event-driven stats/reporting.
- For parallel edits, verify process-based pool configuration rather than thread terminology in comments; global fixture plugins are ignored in worker bootstrap, and serialized functions are omitted.
- Never hand-edit mocha.js or mocha.js.map; Rollup owns them, and test-browser performs clean/build before browser checks. Runtime signature checks use tsc with noEmit over lib.
- When execution is authorized, use smoke plus the nearest node suite for CLI/runtime changes, test-node:reporters or test-node:interfaces for those areas, and test-browser for browser changes; inspect integration's .spec.cjs-only glob before assuming a new .spec.js test runs.
- Preserve captured timer references and named runtime ESM exports; keep docs work in its separate package, and recognize that the main test workflow ignores docs, Markdown, and .github-only changes.
Languages
| Language | Files | Share of source |
|---|---|---|
| JavaScript | 463 | 97% |
| Markdown | 88 | docs/config |
| YAML | 28 | docs/config |
| JSON | 21 | docs/config |
| TypeScript | 12 | 3% |
| CSS | 2 | docs/config |
| HTML | 2 | docs/config |
| TOML | 2 | 0% |
Start reading here
Probable source entrypoint: lib/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
lib/ (65 files)
Repository structure
| Top-level path | Files |
|---|---|
test/ | 425 |
docs/ | 101 |
lib/ | 65 |
.github/ | 30 |
repro/ | 5 |
example/ | 5 |
scripts/ | 3 |
Docs found: AGENTS.mdREADME.mddocs/README.md
Package
package.json — mocha v12.0.3, 54 declared dependencies.
@eslint/js@eslint/markdown@playwright/test@rollup/plugin-alias@rollup/plugin-commonjs@rollup/plugin-json@rollup/plugin-node-resolve@test/esm-only-loader@types/nodebrowser-stdoutchaichokidar
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructions.github/workflows/mocha.yml— GitHub Actions workflow.github/workflows/remove-waiting-label.yml— GitHub Actions workflow.github/workflows/format.yml— GitHub Actions workflow.github/workflows/npm-script.yml— GitHub Actions workflow.github/workflows/stale.yml— GitHub Actions workflow.github/workflows/add-to-project.yml— GitHub Actions workflow.github/workflows/nightly-site-deploy.yml— GitHub Actions workflow.github/workflows/release-please.yml— GitHub Actions workflow
Structural health signals
Average score 8.9/10 across 96 analyzed source files; 4 hotspots surfaced.
test/node-unit/cli/options.spec.js— 922 lines, score 5 (Large file, Deep nesting)lib/runnable.js— 475 lines, score 5.5 (Large file, Deep nesting)lib/runner.js— 1419 lines, score 6.2 (Large file, Deep nesting)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
79db2ee53b862026-10-01 — fix: detect global leaks on every watch rerun (#6368)430901f69cfd2026-10-01 — Fix flaky `--exit` integration timeout by avoiding a hard-coded port (#6364)202bfe18f1542026-10-01 — fix: show built-in reporters in --list-reporters (#6361)8f286b5c33772026-10-01 — fix: show --clear-screen in help command (#6360)51a0cc5530d82026-09-30 — chore(main): release 12.0.3 (#6353)