The six health gates
From the repository root:bun run build remains available for package and Module authoring. Browser smoke runs separately when a change reaches a browser-specific seam.
Unit tests
Every Module ships tests undermodules/<name>/src/__tests__/. The one 86d module create scaffolds checks the factory and nothing else:
What is worth testing
- The factory. It returns the right
idand takes its options. - Controllers. Every state transition, guard, and calculation gets one happy path and one failure path. The failure path is the one that matters.
- Endpoint handlers. Input validation, auth checks, response shape.
- Contracts. When your Module publishes something through
exports.read, test that a consumer receives what you think it does.
One Module at a time
Browser smoke
Playwright lives intests/browser/. One Chromium project runs with zero retries. The suite is deliberately small: it covers authentication and session boundaries, catalog-to-checkout navigation, cart persistence, browser request construction, keyboard focus, mobile overflow, and recovery behavior.
Point BROWSER_STORE_URL at an already running, migrated, seeded Store. It defaults to http://localhost:3000 and fails closed when the target or authenticated session is unavailable.
BROWSER_START_SERVER=1 if Playwright should start the development Store for a local run. CI installs Chromium, builds and starts the production Store, and runs the same suite in its path-filtered workflow.
Do not turn browser smoke into a route inventory, a wall-clock benchmark, or a screenshot matrix. Navigation, storage, focus, responsive layout, browser request construction, and similar browser boundaries belong here. State reducers, validation, calculations, endpoint envelopes, and broad route coverage belong in faster tests.
Direct visual review
For affected UI, inspect the running Store directly in Chrome at 1280 x 720 and 375 x 667, in light and dark appearance. Use the browser-addressable fixture routes to exercise required loading, empty, error, permission, unavailable, and populated states without depending on live provider data. Check keyboard focus, overflow, responsive composition, errors, and recovery paths relevant to the change. Inspect browser console and network failures too. Screenshots can support a dated review, but they are not pixel-diff baselines and never advance a launch or capability gate.Selectors
Prefer role and label selectors. Usedata-testid when the element has no stable accessible locator. A CSS class selector breaks the next time someone touches the styling.
Use web-first assertions. Do not add fixed delays or wait for networkidle; both couple the test to timing that is unrelated to the behavior under test.
Checking a whole change
- Run the focused tests for the package or Module you touched.
- Run all six health gates in repository order.
- Seed the target Store with
bun run db:seed(curated Modules with compiled tables only; requiresDATABASE_URL). - Run
bun run test:browserwhen the change touches a real-browser seam. - For UI work, inspect the affected states in the required Chrome matrix.
Writing a Module that is testable
- Take
ModuleDataServiceas a constructor argument. The runtime hands it toinitasctx.data. Pass it explicitly into your controllers rather than reaching for a global, and your tests get to substitute it. - Keep the logic that does not need I/O separate. A function that takes inputs and returns outputs is trivial to test.
- Mock at the boundary. Swap
fetchitself, not your own wrapper around it. Mocking your wrapper tests your wrapper. - Use
@86d-store/core/test-utilsfor an in-memory data service.