Skip to content

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:

ts
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:

js
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.

json
{
  "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 unless CI is set, and every test that reaches a mock() writes its fixture into .mock-cache. Check git status before you commit, as you would after a local test run. See Record and replay.
  • Stop test:dev before running pnpm test. Both launch holodeck on the same port. The second one retries the bind a few times and then exits with Error: listen EADDRINUSE: address already in use ::1:7358. See The port was taken.
  • If Failed to fetch or Load failed persists while holodeck's banner shows it running, the browser you opened /tests in does not trust the certificate. See Trust a local certificate.
  • Apps that serve their tests with @warp-drive/diagnostic don't hit this. Its launch() starts holodeck from its setup hook every time the suite runs.
  • The page on 4200 still reaches holodeck on 7358, 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 preflight OPTIONS requests.

Released under the MIT License.