policyengine-test-writing
This skill should be used when writing unit tests, integration tests, or test fixtures for PolicyEngine frontend apps, APIs, SDKs, and standalone tools. NOT for country model packages (policyengine-us, policyengine-uk, etc.) — those use YAML-based tests with their own conventions. Covers the Given-When-Then naming convention, fixture extraction, edge case coverage, and the rule that only modified test files should be run. Triggers: "write tests", "add tests", "unit test", "test file", "test coverage", "write a test for", "test this function", "test this component", "given when then", "test fixtures", "mock setup", "edge cases", "test naming", "test convention"
What this skill does
# PolicyEngine Test Writing
Standard conventions for writing tests in PolicyEngine frontend apps, APIs, SDKs, and standalone tools.
These rules apply to every language and framework (Vitest, pytest, etc.) unless a project-specific
override exists.
## Country model packages — use different conventions
**Do NOT apply this skill to country model packages** (`policyengine-us`, `policyengine-uk`,
`policyengine-canada`, etc.). Those repos use YAML-based tests with entirely different structure,
naming, and tooling. For country packages, use these instead:
- **`policyengine-testing-patterns-skill`** (`skills/technical-patterns/policyengine-testing-patterns-skill/SKILL.md`) — YAML test structure, naming conventions (`variable_name.yaml`, `integration.yaml`), period handling, error margins, and quality standards
- **`test-creator` agent** (`agents/country-models/test-creator.md`) — Automated agent that creates comprehensive YAML integration tests for government benefit program implementations
Country model tests are `.yaml` files that live alongside the variables they test, not `.test.ts` or
`.test.py` files in a separate `tests/` directory.
## Core Principles
### 1. Given-When-Then Naming
Every test name follows the pattern `test__given_X_condition__then_Y_occurs`:
```typescript
// TypeScript / Vitest
test("test__given_valid_income__then_tax_is_calculated", () => { ... });
test("test__given_negative_income__then_error_is_thrown", () => { ... });
test("test__given_zero_children__then_ctc_is_zero", () => { ... });
```
```python
# Python / pytest
def test__given_valid_income__then_tax_is_calculated():
...
def test__given_negative_income__then_error_is_thrown():
...
```
Inside the test body, organize code into three clearly commented sections:
```typescript
test("test__given_user_clicks_submit__then_form_is_submitted", async () => {
// Given
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<Form onSubmit={onSubmit} />);
// When
await user.click(screen.getByRole("button", { name: /submit/i }));
// Then
expect(onSubmit).toHaveBeenCalledOnce();
});
```
### 2. One Test File Per Source File
Each source file gets exactly one corresponding test file named `test_FILENAME`:
| Source file | Test file |
|---|---|
| `utils/formatCurrency.ts` | `tests/unit/utils/test_formatCurrency.test.ts` |
| `components/MetricCard.tsx` | `tests/unit/components/test_MetricCard.test.tsx` |
| `lib/api/client.ts` | `tests/unit/lib/api/test_client.test.ts` |
| `services/simulation.py` | `tests/unit/services/test_simulation.py` |
The test file mirrors the source directory structure under a `tests/` root.
### 3. Fixtures Live Separately
All mocks, setup code, patches, constants, and test data must be extracted to a fixture file with the same name in a `fixtures/` directory:
```
tests/
├── fixtures/
│ ├── utils/
│ │ └── test_formatCurrency.ts ← mocks, constants, helpers
│ ├── components/
│ │ └── test_MetricCard.ts
│ └── lib/
│ └── api/
│ └── test_client.ts
├── unit/
│ ├── utils/
│ │ └── test_formatCurrency.test.ts ← imports from fixtures
│ ├── components/
│ │ └── test_MetricCard.test.tsx
│ └── lib/
│ └── api/
│ └── test_client.test.ts
```
**What goes in fixtures:**
- Mock data objects and factory functions
- Descriptive constants (no magic numbers in tests)
- `vi.fn()` / `MagicMock` setup helpers
- Patch targets and mock response builders
- Shared `beforeEach` / `afterEach` setup functions
**What stays in the test file:**
- `describe` / `test` blocks
- The Given-When-Then logic
- `expect` / `assert` statements
Import everything from the fixture:
```typescript
import {
VALID_HOUSEHOLD,
EMPTY_HOUSEHOLD,
mockApiSuccess,
mockApiError,
EXPECTED_TAX_AMOUNT,
} from "@/tests/fixtures/lib/api/test_client";
```
### 4. Test Edge Cases and Failure Paths
Every test file must cover, at minimum:
- **Happy path**: Normal inputs produce expected outputs
- **Boundary values**: Zero, empty string, empty array, min/max values
- **Error cases**: Invalid inputs, network failures, missing data
- **Null/undefined**: What happens with missing or nullable fields
- **Type coercion traps**: String "0" vs number 0, empty object vs null
Structure the `describe` block to make coverage obvious:
```typescript
describe("calculateTax", () => {
// Happy path
test("test__given_valid_income__then_correct_tax_returned", () => { ... });
test("test__given_income_at_bracket_boundary__then_correct_bracket_applied", () => { ... });
// Edge cases
test("test__given_zero_income__then_zero_tax", () => { ... });
test("test__given_negative_income__then_throws_error", () => { ... });
// Error handling
test("test__given_api_timeout__then_error_propagated", () => { ... });
test("test__given_malformed_response__then_fallback_used", () => { ... });
});
```
### 5. Run Only What Changed
After writing or modifying test files, run **only** those specific tests — never the entire suite:
```bash
# TypeScript / Vitest — run specific test file(s)
bunx vitest run tests/unit/utils/test_formatCurrency.test.ts
# Python / pytest — run specific test file(s)
pytest tests/unit/variables/test_income.py -v
```
After tests pass, run formatters and typecheckers **only on modified files**:
```bash
# TypeScript — typecheck and lint only changed files
bunx tsc --noEmit
bunx eslint tests/unit/utils/test_formatCurrency.test.ts tests/fixtures/utils/test_formatCurrency.ts
# Python — format and lint only changed files
black tests/unit/variables/test_income.py tests/fixtures/variables/test_income.py
ruff check tests/unit/variables/test_income.py tests/fixtures/variables/test_income.py
```
**Never run the full test suite or full linter unless explicitly asked.** Large codebases take minutes to lint/test; running everything wastes time and produces noise unrelated to the changes.
## Framework-Specific Notes
### Vitest (TypeScript / React)
```typescript
import { describe, test, expect, vi, beforeEach } from "vitest";
```
- Use `vi.fn()` for mocks, `vi.mock()` for module mocks
- Use `vi.clearAllMocks()` in `beforeEach`
- For React components, prefer accessibility selectors (`getByRole`, `getByLabelText`) over test IDs
- Use `userEvent.setup()` for user interactions (not `fireEvent`)
- Use `waitFor` for async state updates
### pytest (Python)
```python
import pytest
from unittest.mock import MagicMock, patch
```
- Use `@pytest.fixture` for setup, import from fixture files
- Use `@pytest.mark.parametrize` for data-driven tests
- Use `pytest.raises(ExceptionType)` for error assertions
- Mark slow tests with `@pytest.mark.slow`
## What to Test
- Public API surface (exported functions, component props, class methods)
- State transitions and side effects
- Data transformations and calculations
- Error handling and recovery paths
- Boundary conditions and edge cases
## What NOT to Test
- Third-party library internals (Recharts rendering, Mantine components, pandas operations)
- Private implementation details that may change
- CSS/styling (unless testing conditional class application)
- Simple pass-through getters with no logic
## Detailed Reference
For fixture best practices, mock patterns, and accessibility selector priority, consult:
- **`references/fixture-patterns.md`** — Comprehensive fixture organization and mock examples
Related in Design
contribute
IncludedLocal-only OSS contribution command center. Auto-refreshes the user's in-flight PR and issue state on invoke so conversations start with full context — no need to brief Claude on what's in flight. Helps the user find issues to contribute to on GitHub, builds per-repo dossiers of what each upstream expects (CLA, DCO, branch convention, AI policy, draft-first, review bots, issue templates), runs deterministic gates before any external action so AI-assisted contributions don't reach maintainers as slop. State is markdown-only: candidate files at ~/.contribute-system/candidates/, repo dossiers at ~/.contribute-system/research/, append-only event log at ~/.contribute-system/log.jsonl. No database, no cloud calls. Use when the user asks about their PRs / issues / contributions, wants to find new work to take on, claim an issue, build/refresh a repo's dossier, or draft a Design Issue or PR. Trigger with "/contribute", "what's my PR status", "find a contribution", "claim issue X", "draft a Design Issue for Y", "refresh dossier for Z".
architectural-analysis
IncludedUser-triggered deep architectural analysis of a codebase or scoped subtree across eight modes — information architecture, data flow, integration points, UI surfaces, interaction patterns, data model, control flow, and failure modes. This skill should be used when the user asks to "diagram this codebase," "map the architecture," "show the data flow," "give me an ERD," "trace control flow," "find the integration points," "verify the layout pattern," "audit the UX architecture," or any similar request whose primary deliverable is mermaid diagrams plus cited reports under docs/architecture/. Dispatches haiku/sonnet sub-agents in parallel for per-mode exploration, then verifies every citation mechanically before any node lands in a diagram. Not for one-off prose explanations of code (use code-explanation) or for high-level system design from scratch (use system-design).
mcp
IncludedModel Context Protocol (MCP) server development and tool management. Languages: Python, TypeScript. Capabilities: build MCP servers, integrate external APIs, discover/execute MCP tools, manage multi-server configs, design agent-centric tools. Actions: create, build, integrate, discover, execute, configure MCP servers/tools. Keywords: MCP, Model Context Protocol, MCP server, MCP tool, stdio transport, SSE transport, tool discovery, resource provider, prompt template, external API integration, Gemini CLI MCP, Claude MCP, agent tools, tool execution, server config. Use when: building MCP servers, integrating external APIs as MCP tools, discovering available MCP tools, executing MCP capabilities, configuring multi-server setups, designing tools for AI agents.
react-native-skia
IncludedDesign, build, debug, and optimise high-polish animated graphics in React Native or Expo using @shopify/react-native-skia, Reanimated, and Gesture Handler. Use when the user wants canvas-driven UI, shaders, paths, rich text, image filters, sprite fields, Skottie, video frames, snapshots, web CanvasKit setup, or performance tuning for custom motion-heavy elements such as loaders, hero art, cards, charts, progress indicators, particle systems, or gesture-driven surfaces. Also use when the user asks for fluid, glow, glass, blob, parallax, 60fps/120fps, or GPU-friendly animated effects in React Native, even if they do not explicitly say "Skia". Do not use for ordinary form/layout work with standard views.
plaid
IncludedProduct Led AI Development — guides founders from idea to launched product. Six capabilities: Idea (discover a product idea), Validate (pressure-test the idea against fatal flaws, problem reality, competition, and 2-week MVP feasibility), Plan (vision intake + document generation), Design (translate image references into a design.md spec), Launch (go-to-market strategy), and Build (roadmap execution). Use when someone says "PLAID", "plaid idea", "help me find an idea", "product idea", "idea from my business", "idea from my expertise", "plaid validate", "validate my idea", "pressure-test", "is this idea good", "find fatal flaws", "validate the problem", "plan a product", "define my vision", "generate a PRD", "product strategy", "plaid design", "design from image", "translate image to design", "create design.md", "extract design tokens", "plaid launch", "go-to-market", "launch plan", "GTM strategy", "launch playbook", "plaid build", "build the app", "start building", or "execute the roadmap".
nextjs-framer-motion-animations
IncludedAdds production-safe Motion for React or Framer Motion animations to Next.js apps, including reveal, hover and tap micro-interactions, whileInView, stagger, AnimatePresence, layout and layoutId transitions, reorder, scroll-linked UI, and lightweight route-content transitions. Use when the user asks to add, refactor, or debug Motion or Framer Motion in App Router or Pages Router codebases, especially around server/client boundaries, reduced motion, LazyMotion, bundle size, hydration, or route transitions. Avoid for GSAP-style timelines, WebGL or 3D scenes, heavy scroll storytelling, or CSS-only effects unless Motion is explicitly requested.