write-tests

Installation
SKILL.md

Write tests

Where tests live

  • Next to the code: src/lib/refs.ts → src/lib/refs.test.ts. A command group has one test file for the group or one per subcommand (src/commands/label/label.test.ts, src/commands/project/reorder.test.ts). Vitest, globals on, restoreMocks: true, so spies restore themselves: no manual .mockRestore().
  • Command tests mock the SDK client: vi.mock('../../lib/api/core.js', () => ({ getApi: vi.fn() })) at the top, mockApi = setupApiMock() in beforeEach, a program from createTestProgram(registerXxxCommand), then program.parseAsync(['node', 'td', ...]). Capture output with captureConsole() or captureStream(). Read src/commands/label/label.test.ts for the shape.
  • src/index.integration.test.ts builds the CLI and runs dist/index.js, for argv handling, global flags and JSON errors end to end.
  • vi.mock('chalk') uses the manual mock in src/__mocks__/chalk.ts, which makes styling a passthrough so tests read plain text.
  • Agent tooling tests live in .agents/scripts/ and run copies of the tools in throwaway repos (.agents/scripts/lib/test-repo.ts).

Helpers

From src/test-support/: createMockApi, setupApiMock, fixtures, makeFilter, createProjectProgram. From @doist/cli-core/testing: createTestProgram, captureConsole, captureStream, describeEmptyMachineOutput. Build entities from fixtures instead of by hand; extend it when a test needs a new shape.

The quality bar

Read references/test-rules.md before writing or reviewing a test. In short: a test counts only if it can fail. It asserts outcomes, takes expected values from outside the code under test, mocks only real boundaries, and you saw it fail once.

Done when each new test has failed once for the right reason, and then passes.

Installs
4
GitHub Stars
332
First Seen
5 days ago