Holodeck in dev mode
An app whose dev server also serves its test page can run the suite in the browser. You start vite with pnpm start and open /tests, which is http://localhost:4200/tests in the examples here. If your tests use holodeck, that page fails when the suite runs, even though pnpm test passes.
This recipe starts holodeck next to the dev server so both ways of running the suite work.
Before you start
This page assumes holodeck already works under pnpm test, with testem launching it as Server setup describes. The examples use the fixed port 7358. The browser you open /tests in has to trust the local certificate from Trust a local certificate. tests/test-helper.js points requests at the mock server:
import { setConfig } from '@warp-drive/holodeck';
import { setBuildURLConfig } from '@warp-drive/utilities';
const MOCK_HOST = 'https://localhost:7358';
setBuildURLConfig({ host: MOCK_HOST, namespace: 'api' });
setConfig({ host: MOCK_HOST });Why the dev server fails
testem.js is the only thing that calls holodeck.launchProgram, and only pnpm test runs testem. vite serves the test page but starts nothing on 7358, so every call to mock() fails to reach the server. The page reports two errors per test:
Promise rejected during "the feed renders, and composing a post reloads it": Failed to fetch
Global afterEach failed on the feed renders, and composing a post reloads it: Error: Holodeck: this test declared mocks it never requested.
GET api/users?page%5Bsize%5D=100&sort=handle (mocked 1, requested 0)
A mock that is never requested proves nothing. Remove it, or make the request it describes.Failed to fetch is how Chrome reports the unreachable server. Safari reports it as Load failed. The second error follows from the first: the test declared the mock, but its request never reached a server. The mock itself is fine.
Add a launcher script
Create scripts/holodeck.mjs in the app:
import holodeck from '@warp-drive/holodeck';
await holodeck.launchProgram({ port: 7358 });The server runs in a worker thread, which keeps the process alive after launchProgram resolves. Holodeck handles Ctrl+C itself and prints Holodeck program ended, so the script needs no signal handling.
The port has to match in three places: this script, testem.js, and MOCK_HOST in tests/test-helper.js. Holodeck binds only the port it is given, and the browser only knows the address in MOCK_HOST. If yours differ from 7358, copy the port from testem.js.
Run it with the dev server
Add a script to package.json that starts both processes with concurrently. If your app doesn't have it yet, add it with pnpm add -D concurrently.
{
"scripts": {
"start": "vite",
"test": "vite build --mode development && testem ci --port 0",
"test:dev": "concurrently --names vite,holodeck --kill-others \"vite\" \"node ./scripts/holodeck.mjs\""
}
}Run pnpm test:dev, then open http://localhost:4200/tests. The holodeck banner prints first:
[holodeck] Holodeck Access Granted
[holodeck] program: web
...
[holodeck] Serving Holodeck HTTP Mocks from https://localhost:7358
...
[vite] ➜ Local: http://localhost:4200/--kill-others stops Vite when holodeck exits, and holodeck when Vite exits. You never end up with a dev server whose tests fail because the mock server quietly died. Ctrl+C stops both.
The package script runs from the app's own directory. That matters, because launchProgram reads package.json and writes .mock-cache in the current working directory.
start stays as it was. Keep it separate if you use it for day-to-day work against a real API, so it doesn't need the holodeck certificate.
Things to know
- A dev run records fixtures, just like a local
pnpm test. Holodeck records unlessCIis set, and every test that reaches amock()writes its fixture into.mock-cache. Checkgit statusbefore you commit, as you would after a local test run. See Record and replay. - Stop
test:devbefore runningpnpm test. Both launch holodeck on the same port. The second one retries the bind a few times and then exits withError: listen EADDRINUSE: address already in use ::1:7358. See The port was taken. - If
Failed to fetchorLoad failedpersists while holodeck's banner shows it running, the browser you opened/testsin does not trust the certificate. See Trust a local certificate. - Apps that serve their tests with
@warp-drive/diagnosticdon't hit this. Itslaunch()starts holodeck from itssetuphook every time the suite runs. - The page on
4200still reaches holodeck on7358, so every mocked request is cross-origin. Common setups puts the mock server on the page's own origin, from Vite, from testem, or behind Caddy, with no preflightOPTIONSrequests.