Tests

Updated Jul 27, 2026

Unit tests (Bun)

Unit tests run under Bun's built-in test runner. Use one of the following:

  • bun run test:unit
  • bun test test/unit
  • bun run test:coverage (writes text output and an LCOV (line coverage) report under coverage/)
  • make coverage
  • For roadmap item 9.1.1, run focused coverage with:
  bun test --coverage \
    test/unit/command-registry.test.ts \
    test/unit/command-registry-validation.test.ts \
    test/unit/context-key-service.test.ts \
    test/unit/runtime-plugin-settings.test.ts

Until Bun branch-threshold enforcement is wired for this repository, use the current codebase target from docs/velocetty-hyper-codebase.md §6.6.7.2 as the local pass/fail basis for touched core modules: at least 60% line coverage and 50% function coverage as the Bun-reported proxy for the documented 50% branch target.

make test executes the shared unit suite through bun run test:unit:run, which runs with --concurrent (maximum concurrency) as the default. Roadmap item 9.3.1 removed the dedicated bootstrap-process quarantine by moving renderer bootstrap assertions behind injected seams instead of file-scope module mocks; roadmap item 9.3.2 removed the serialized guardrail from the default local and CI unit gate; and roadmap items 9.3.3 through 9.3.7 hardened test-suite isolation so explicit concurrency can be the default.

When checking for order-dependent regressions, replay the unit suite with fixed seeds and explicit concurrency:

bun test --concurrent --randomize --seed 2444615283 test/unit
bun test --concurrent --randomize --seed 1337 test/unit
bun test --concurrent --randomize --seed 20260306 test/unit

To obtain a serialized reproduction path while diagnosing a suspected cross-file race, use one of the explicit diagnostic scripts instead of changing the default gate:

bun run test:unit:serialized
bun run test:unit:serialized:shuffled

For focused stress testing of specific suites under explicit concurrency, use targeted runs:

bun test --concurrent \
  test/unit/rpc-client.test.ts \
  test/unit/term-report-renderer.test.ts

This command supplements the default make test path and the seeded randomized reruns above.

For roadmap item 9.3.4 and similar filesystem-fixture isolation work, use a focused explicit-concurrency stress run against the directory-bootstrap helper suite:

bun test --concurrent test/unit/ensure-directory-path.test.ts

Keep temporary-directory ownership and teardown scoped to each test, either directly in the test or via a helper that returns per-test cleanup, so ownership is not shared. Do not use a shared file-scope cleanup queue for temporary roots in suites that must survive explicit --concurrent runs.

For roadmap item 9.3.5 and similar snapshot/bootstrap or CLI-config isolation work, use a focused explicit-concurrency stress run against the snapshot and CLI behaviour suites:

bun test --concurrent \
  test/unit/v8-snapshot-util.test.ts \
  test/unit/cli-api-behaviour.test.ts

Keep snapshot/bootstrap state isolated from globalThis during unit tests. Prefer explicit bootstrap helpers that accept a test-owned runtime host and return a restoration handle for any patched module loader state, so each test can clean up the loader it installed without relying on file-scope teardown.

For CLI-config tests, do not share file-scope mutable mock state, shared process.env mutation, or module-scope config-path capture across tests. Prefer a per-test API factory with injected filesystem, registry, and environment state so explicit --concurrent runs keep request history, config-path resolution, and parsed-plugin state isolated per test instance.

For roadmap item 9.3.6 and similar concurrency-hotspot cleanup, use a focused explicit-concurrency stress run against the remaining long-lived mock and global hotspots:

bun test --concurrent \
  test/unit/runtime-tab-provider-registration.test.ts \
  test/unit/command-registry-compat.test.ts \
  test/unit/config-import-json5.test.ts

Suites that must survive that probe must not rely on afterAll(...) to tear down mock.module(...) registrations, temporary window installs, or other process-global shims. Prefer per-test harness helpers that either return a cleanup callback or accept injected dependencies directly. When a module under test captures transport, config, or filesystem state at module scope, add the smallest behaviour-preserving factory seam needed, so tests can provide test-owned dependencies without long-lived module mocks.

For roadmap item 9.3.7 and similar timer/logger-dependent module work, use injected seams instead of process-global mutations:

  • Pass timer implementations (setTimeout, clearTimeout, setInterval, clearInterval) through component props or function options rather than replacing globalThis methods.
  • Pass logger implementations (console.error, etc.) through function options rather than replacing console methods.
  • Keep global fallbacks for production code when seams are not provided.
  • Test with explicit --concurrent stress runs to verify isolation:
  bun test --concurrent test/unit/notification.test.ts
  bun test --concurrent test/unit/updater.test.ts

Suites that rely on timer or logger seams must not mutate global state during test execution; instead, provide test-doubles through the module's public interface.

End-to-end (E2E) tests (layered strategy)

End-to-end tests are split into two lanes and require packaged binaries in dist/.

Fast lane (required on pull requests):

  • Run bun run test:e2e:fast (or bun run test:e2e).
  • Executes Bun-driven smoke checks in test/e2e/.
  • Asserts renderer readiness and fails on critical renderer console errors.
  • Supports E2E_DRIVER=playwright|spawn overrides; CI defaults to spawn-mode markers, with a macOS packaged-launch fallback that accepts missing renderer-ready marker output only when the process remains alive through an additional stability window bounded by remaining test-timeout budget.
  • Supports E2E_DEBUG=1 for verbose launch logs and E2E_CAPTURE=1 for screenshot capture.

Deep lane (scheduled and release validation):

  • Run bun run test:e2e:deep.
  • Executes Playwright Test under Node.js using playwright.e2e.config.ts and test/e2e-deep/.
  • Installs Playwright Chromium on demand before execution.
  • Validates the first interaction-path scenario (terminal input and rendered output).
  • Retains full diagnostics on failures: stdout/stderr logs, renderer console logs, screenshots, and traces.
  • Runs in CI on Linux for scheduled checks, manual workflow_dispatch, and pushes to master and canary.
  • Deep-lane failures on master and canary are release-blocking.

For screen readers: The following sequence diagram shows fast-lane execution, including main-process readiness/error markers consumed by Bun E2E assertions.

ConsoleElectron_rendererElectron_main_processBun_test_runnerConsoleElectron_rendererElectron_main_processBun_test_runnerDevrun bun run test:e2e:fastset RUN_E2E=1launch packaged Electron appload renderer URLdid-finish-loadRUN_E2E == 1log [e2e] renderer-readysend init(uid, profileName)console-message(level,message,line,sourceId)if level >= errorlog [e2e][renderer-error] sourceId:line messagewait for renderer-ready markerrenderer-ready observedassert readiness and no critical renderer-error logsreport fast-lane E2E resultDev

Figure 1: Fast-lane E2E sequence from Bun invocation to readiness/error assertions.

For screen readers: The following sequence diagram shows deep-lane execution through Playwright CLI/Test, including interaction-path assertion and artefact reporting.

ConsoleElectron_rendererElectron_main_processPlaywright_Test_runnerPlaywright_CLIBun_cliConsoleElectron_rendererElectron_main_processPlaywright_Test_runnerPlaywright_CLIBun_cliDevrun bun run test:e2e:deeptest:e2e:prepare (rimraf dist/tmp/root/test)install chromiumchromium installedtest -c playwright.e2e.config.tsrun tests in test/e2e-deeplaunch packaged Electron appload renderer URLdid-finish-loadlog [e2e] renderer-readysend init(uid, profileName)type sentinel command into terminalconsole-message eventslog high severity errors as [e2e][renderer-error]wait for rendered output containing sentinelterminal output with sentinelassert interaction pathreport test resultgenerate report and artefacts on failureDev

Figure 2: Deep-lane E2E sequence from Bun command orchestration to Playwright interaction and reporting.

Before either lane, build packaged artefacts with bun run dist if they do not already exist.