From harness-claude
Tests XState machines with direct state transition assertions, isolated guards/actions, and @xstate/test for path coverage. Use for verifying transitions, context updates, and React integrations.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Test XState machines with direct state transition assertions and model-based testing for path coverage
Defines XState state machines with createMachine for explicit states, transitions, context, and events. For modeling complex UI flows like forms, wizards, authentication, and workflows preventing illegal transitions.
Builds, tests, and debugs event-driven state machines with EventMachine Laravel package for declarative workflows, parallel states, child delegation, event sourcing, timers, and HTTP endpoints.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Test XState machines with direct state transition assertions and model-based testing for path coverage
@xstate/test to generate test paths that cover all states and transitions.// auth.machine.test.ts — direct transition testing
import { createActor } from 'xstate'; // v5
import { authMachine } from './auth.machine';
describe('auth machine', () => {
it('transitions from idle to authenticating on LOGIN', () => {
const actor = createActor(authMachine).start();
actor.send({ type: 'LOGIN', email: 'a@b.com', password: '123' });
expect(actor.getSnapshot().matches('authenticating')).toBe(true);
actor.stop();
});
it('ignores LOGOUT in idle state', () => {
const actor = createActor(authMachine).start();
actor.send({ type: 'LOGOUT' });
expect(actor.getSnapshot().matches('idle')).toBe(true);
actor.stop();
});
it('stores user on successful authentication', async () => {
const testMachine = authMachine.provide({
actors: {
authenticate: fromPromise(async () => ({ id: '1', name: 'Alice' })),
},
});
const actor = createActor(testMachine).start();
actor.send({ type: 'LOGIN', email: 'a@b.com', password: '123' });
// Wait for the invoke to complete
await new Promise((resolve) => {
actor.subscribe((snapshot) => {
if (snapshot.matches('authenticated')) resolve(undefined);
});
});
expect(actor.getSnapshot().context.user).toEqual({ id: '1', name: 'Alice' });
actor.stop();
});
});
// Testing guards in isolation
import { canRetry } from './auth.guards';
describe('canRetry guard', () => {
it('allows retry when retries < 3', () => {
expect(canRetry({ context: { retries: 2 } })).toBe(true);
});
it('blocks retry when retries >= 3', () => {
expect(canRetry({ context: { retries: 3 } })).toBe(false);
});
});
v4 testing style:
import { interpret } from 'xstate';
const service = interpret(machine).start();
service.send('LOGIN');
expect(service.state.matches('authenticating')).toBe(true);
service.stop();
@xstate/test model-based testing: Generates test paths that cover all reachable states:
import { createTestModel, createTestMachine } from '@xstate/test';
const testMachine = createTestMachine({
initial: 'idle',
states: {
idle: {
on: { LOGIN: 'loading' },
meta: {
test: async (page) => {
await expect(page.getByText('Sign in')).toBeVisible();
},
},
},
loading: {
on: { SUCCESS: 'dashboard' },
meta: {
test: async (page) => {
await expect(page.getByText('Loading')).toBeVisible();
},
},
},
dashboard: {
meta: {
test: async (page) => {
await expect(page.getByText('Welcome')).toBeVisible();
},
},
},
},
});
const model = createTestModel(testMachine);
const paths = model.getShortestPaths();
paths.forEach((path) => {
it(path.description, async () => {
await path.test({
/* page or test context */
});
});
});
What to test for each machine:
Mocking services in v5:
const testMachine = machine.provide({
actors: { fetchData: fromPromise(async () => mockData) },
actions: { logAnalytics: () => {} }, // No-op in tests
});
https://stately.ai/docs/testing