Skip to content
Back to skills

Write Tests

ASecurity

Write Vitest unit/integration tests OR Playwright e2e tests for an existing source file (server action, page, component, query). Reads the project''s wired test framework from `.workflow/meta.json#stack.test`, follows the mocking patterns already present in `vitest.setup.ts` and any sibling test files. Use when the user says "scrivi i test per X", "test per la server action Y", "e2e per /clienti", "unit test per il componente Z", "add tests for <file>", or after `screenshot-to-page` / `module...

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
developmentbashsqlreactdebuggingapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 8 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add lukedj78/dev-flow --skill write-tests --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Write Tests?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Write Tests
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lukedj78-write-tests/badge)](https://www.skillsdirectory.com/skills/lukedj78-write-tests)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: write-tests
description: 'Write Vitest unit/integration tests OR Playwright e2e tests for an existing source file (server action, page, component, query). Reads the project''s wired test framework from `.workflow/meta.json#stack.test`, follows the mocking patterns already present in `vitest.setup.ts` and any sibling test files. Use when the user says "scrivi i test per X", "test per la server action Y", "e2e per /clienti", "unit test per il componente Z", "add tests for <file>", or after `screenshot-to-page` / `module-add` runs and the user wants coverage. Not for: scaffolding the test infrastructure (`module-add test`), running QA on a deployed site (`qa`), methodology guidance (`superpowers:test-driven-development`), or an Expo/RN app (`rn-write-tests`).'
---

# write-tests — generate tests for one file at a time

This skill writes the test file that should sit next to a piece of source code. One invocation = one source file → one test file. It does **not** install test infrastructure (that's `module-add test`), does **not** debug failing tests (that's `superpowers:systematic-debugging`), and does **not** run an HTTP-based QA pass (that's `qa`).

The unit of work is **deliberately small** because tests rot when they're auto-generated in bulk. A single file at a time forces the user to read what was written and accept it before moving to the next.

## When this skill applies

- The user names a source file: "scrivi i test per `lib/server/clienti.ts`", "test per `app/checkout/page.tsx`", "e2e per /scadenze".
- The orchestrator routes here from `module_added` (test infrastructure exists) when the user wants per-feature coverage.
- The project has a wired test stack — `meta.json#stack.test` is set, OR `vitest.config.ts` + `playwright.config.ts` exist at the root.

## Contract

This skill follows the dev-flow contract — see `references/contracts.md`. Key facts:
- Reads `<root>/.workflow/meta.json#stack.test` to confirm the test framework is wired.
- Reads the project's existing `vitest.setup.ts`, `vitest.config.ts`, `playwright.config.ts`, and any sibling test files to learn the mocking style — **does not re-prescribe** patterns from scratch.
- Writes a single test file alongside the source (`__tests__/<name>.test.ts` for unit, `e2e/<name>.spec.ts` for e2e).
- Does NOT bump `phase` — tests are continuous work, not a phase transition.
- Appends to `meta.json#history` with the source file path, the test path, and the test type.
- **Idempotent**: if the test file already exists, ask the user — either regenerate, append missing cases, or abort. Never silently overwrite.

## Workflow

### Step 1 — Verify prerequisites

Read `<project-root>/.workflow/meta.json`:

- `stack.framework` must be `next`, or `monorepo` with the test aimed at `apps/web/`. On `expo-rn` (or a file under `apps/mobile/`), stop and hand off to **`rn-write-tests`**: `stack.test` alone does not tell the two apart — `jest-expo+rntl` is a valid value too — and this skill's Vitest/Playwright patterns are wrong for React Native.
- `stack.test` must be set (e.g., `"vitest+playwright"`). If `null`, stop and tell the user: *"Test infrastructure not wired. Run `module-add test` first, then come back."*
- `phase` should be `module_added` or later. If earlier, the project might not have anything testable yet — warn and ask whether to proceed.

If `stack.test` is unset but `vitest.config.ts` exists at the project root, the user installed Vitest manually outside the contract. Proceed, but log this in the user-facing summary so they can re-run `module-add test` to formalize.

### Step 2 — Identify the source file + classify it

The user names a file. Classify it by path + content:

| Pattern | Classification | Reference |
|---|---|---|
| `lib/server/<domain>.ts` (contains `"use server"` or exported async functions returning `ActionResult`) | **Server action** | `references/test-server-action.md` |
| `lib/queries/<domain>.ts` (async functions reading from `@/lib/db`) | **Query** | `references/test-query.md` |
| `app/<route>/page.tsx` or `app/<route>/[...slug]/page.tsx` | **Page (e2e)** | `references/test-page-e2e.md` |
| the same, on a project whose `stack.test` carries the `e2e` layer | **Page (agentic e2e)** | `references/e2e-agentic.md` |
| `components/**/*.tsx` containing `"use client"` and interactive logic | **Component (RTL)** | `references/test-component.md` |
| `components/ui/*.tsx` (shadcn primitives) | **Skip** — primitives are tested upstream | — |
| Anything else | **Ask the user** what kind of test they want | — |

If the file matches multiple patterns (e.g., a server action that internally uses a query), test as **server action** — the action is the public surface, the query is implementation detail.

### Step 3 — Read the project's existing test patterns

Before writing anything, read **at least one existing test file of the same type** in the project. The mocking style, the import order (mocks before imports), the naming convention, and the assertion library are already pinned by what's there. Copy the style. Don't introduce a new pattern.

If no test of that type exists yet:
- For server actions: read `references/test-server-action.md` for the canonical pattern.
- For pages e2e: read `references/test-page-e2e.md` — **unless** `stack.test` carries `e2e`, the
  agentic runner, which is `references/e2e-agentic.md`. That file is also where `rn-write-tests`
  reads the mobile half from: one technique, one copy.
- For components: read `references/test-component.md`.
- For queries: read `references/test-query.md`.

Also read `vitest.setup.ts` to see what's globally mocked (typically `next/navigation`, `next/cache`). Don't re-mock those in individual tests.

### Step 4 — Read the source file

Read the source file end-to-end. Identify:

- **For server actions**: the action's input schema (Zod), the success path, every business-error branch (what does it return `{ ok: false }` for?), every system-error branch (what does it `throw`?), and the auth dependencies (`getCurrentTenantId`, `getCurrentUserId`).
- **For queries**: the input arguments, the SQL/Drizzle calls, the shape of the return, the not-found / empty-result handling.
- **For pages**: the rendered structure (which sections, which interactive elements, which links), the data dependencies (what `getX()` queries fire), the URL params if dynamic.
- **For components**: the prop surface, the interactive states (`useState`, event handlers), the conditional rendering branches.

Make a list of test cases **before writing the test**. The list is the spec — the test file is just the encoding.

### Step 5 — Check for existing test file

Look for:
- Server actions: `lib/server/__tests__/<name>.test.ts`
- Queries: `lib/queries/__tests__/<name>.test.ts`
- Components: `components/<group>/__tests__/<name>.test.tsx` (or co-located: `components/<group>/<name>.test.tsx`)
- Pages: `e2e/<route-slug>.spec.ts`

If exists:
- **Ask** the user: regenerate from scratch, add only the missing cases, or abort?
- **Default**: add only the missing cases. Open the existing file, run the spec list against the existing `it("...")` blocks, and append the gaps.
- **Never silently overwrite**.

### Step 6 — Write the test

Apply the relevant reference template (from `references/`), filling in the project-specific values:

- Real source paths (resolved from `meta.json` or by reading the project tree).
- Real mock shapes (read from existing tests if any).
- Real assertion patterns (match what's already there for consistency).

The reference templates show the **canonical shape** for each test type. Adapt — don't paste verbatim.

### Step 7 — Run the new test in isolation

After writing:

```bash
pnpm vitest run <test-file>          # for unit tests
pnpm playwright test <test-file>     # for e2e
```

If it fails, read the error:
- **Mock missing**: a dependency wasn't mocked. Add the mock at the top of the file, re-run.
- **Schema drift**: the source file's signature doesn't match what the test assumed. Re-read source, re-write the relevant `it()` block.
- **Test passes but coverage is wrong**: this skill doesn't gate on coverage — leave a comment if a branch wasn't hit and tell the user.

If the failure is in the **source code** (e.g., the action throws unexpectedly), stop. Tests revealing real bugs is the point — surface the bug to the user, don't paper over it.

### Step 8 — Update state and report

Update `.workflow/meta.json`:

- **Don't bump phase**. Tests are continuous.
- Append history:
  ```json
  {
    "skill": "write-tests",
    "ran_at": "<now>",
    "inputs": {"source": "lib/server/clienti.ts", "type": "server-action"},
    "outputs": ["lib/server/__tests__/clienti.test.ts"],
    "phase_before": "<unchanged>",
    "phase_after": "<unchanged>"
  }
  ```

Tell the user:
- The test file path.
- The list of cases covered (one bullet per `it("...")`).
- The list of cases **deliberately not covered** (e.g., "auth-mocking pattern requires `module-add auth` to have run; one test marked `it.todo`") so they know the gap.
- The command to run it: `pnpm vitest run <path>` or `pnpm playwright test <path>`.

## Important constraints

- **One source file per invocation.** "Scrivi i test per tutto" is N runs — keep history clean and let the user accept each.
- **Read existing tests first.** If `lib/server/__tests__/practices.test.ts` already exists with a specific mocking style, the new `clienti.test.ts` matches that style. Consistency over canonical-template-from-this-skill.
- **Don't introduce new test deps.** If the project has Vitest + Playwright, work with those. Don't suggest `jest`, `mocha`, `cypress`, `vitest-mock-extended`, etc.
- **Don't fix the source code.** If a test reveals a bug, surface it. Don't silently rewrite the source to make the test pass.
- **Real assertions, not snapshots by default.** Snapshot tests rot. Prefer specific assertions (`expect(result.ok).toBe(false)`, `expect(page.locator("h1")).toContainText("...")`). Snapshots only when the user explicitly asks.
- **A passing unit test does not mean the component survives real data.** Asserting the logic and
  rendering a component against *kind* data are different tests, and the second one is the one users
  hit: a hyphenated surname, an unbreakable email, a count of exactly 1, an empty list, a label 30%
  longer in translation. **`references/worst-case-data.md`** is that operation — the catalog of
  plausible values, the failure-signature table (symptom → cause → fix), and the dev-only
  Demo/Worst-case toggle. Reach for it when the user says "stress test this", "find the edge cases",
  or when a component renders anything a person typed. The fixture it produces **stays in the repo as
  a regression test**.
- **Skip what's not testable cheaply.** Pages with WebGL canvases, third-party iframes, or external API calls without a mock surface — write a `test.skip` with a comment explaining why instead of writing a brittle test. Honesty over coverage theater.

## Cross-skill dependencies

- **Requires** `module-add test` to have run (the test infrastructure has to exist).
- **Plays nice with** `module-add db`, `module-add auth` — the smoke tests in `module-add test` mock these; this skill follows the same pattern.
- **Starts from a proven journey** when there is one: a feature file of the project's verification manual (`.claude/skills/verify-<app>/features/`, from `app-verification`) already names the entry point, the accessible names and the end state — the Playwright or e2e test is that recipe made repeatable.
- **Pre-`module-add ci`**: tests written by this skill run in CI once `module-add ci` runs (the GHA workflow added there picks up `pnpm test:run` and `pnpm test:e2e`).

## What this skill does NOT do

- **Does not install test packages.** That's `module-add test`. If `vitest` isn't in `package.json`, this skill stops and routes there.
- **Does not run a full test suite.** It runs only the new test in isolation to verify it passes. Full suite runs are CI's job.
- **Does not fix failing tests.** A failing test is a signal, not a problem. Surface it to the user; the user decides whether to fix the test or fix the source.
- **Does not generate coverage reports.** Coverage is a separate concern; the user can wire `vitest --coverage` themselves once they have enough tests.
- **Does not write tests for shadcn primitives or third-party libs.** Test the project's own logic, not the dependencies.

Files in this skill

  • SKILL.md12.3 KB
  • references/contracts.md80.5 KB
  • references/e2e-agentic.md7.8 KB
  • references/test-component.md12.2 KB
  • references/test-page-e2e.md11 KB
  • references/test-query.md6.3 KB
  • references/test-server-action.md7.5 KB
  • references/worst-case-data.md11.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.