Skip to content

Write your first test

Start with a workflow you care about. We’ll use saving a contact here; swap in your app’s route, labels and expected result.

You’ll need Node 24+. Install from GitHub for now, then download Chromium once:

Terminal window
npm install --save-dev github:Robdel12/dyno
npx dyno init
npx dyno install

init adds a config and an example test without overwriting existing files. Commit your lockfile so local runs and CI use the same Dyno revision.

Dyno currently pins Interactor fixes that are not yet on the registry. pnpm 11 blocks Git subdependencies and their prepare builds by default. Projects that permit this Git source need the following in pnpm-workspace.yaml, alongside their existing build entries:

blockExoticSubdeps: false
allowBuilds:
interactor.js: true

blockExoticSubdeps changes the project’s whole dependency-source policy; the build permission above only allows Interactor. See pnpm’s source policy before choosing that setting.

In dyno.config.js, set the command that starts your app and the URL it serves:

export default {
testDir: 'tests/browser',
webServer: {
command: 'npm run dev',
url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI,
},
};

Dyno starts that command for you. Locally, reuseExistingServer lets you use an app that’s already running; Dyno leaves that server alone when it stops.

Replace the example test in tests/browser:

import { describe, test } from 'dyno';
describe('Contacts', () => {
test('saves a contact', async ({ app, I }) => {
await app.goto('/contacts/new');
await I.find('Name').then.fill('Robert');
await I.click('Save');
await I.find('$([role=status])').text('Contact saved');
});
});

Interactor waits for the expected UI. Await each action and assertion, and keep tests inside a named describe.

Terminal window
npx dyno doctor
npx dyno --ui

doctor checks your config, test bundle and browser setup. --ui runs the test with the app visible. Use npx dyno for a headless run.

Next, learn the interaction basics or share workflows with custom interactors. JavaScript, JSX, TypeScript and TSX work; run your own type checker for TypeScript.