.agents/skills/testing/SKILL.md
Test file conventions for setup functions, factory patterns, test organization, type testing, and naming. Use when the user says "write tests", "add a test", "fix this test", or when writing or modifying *.test.ts files, creating test setup functions, or reviewing test structure.
npx skillsauth add epicenterhq/epicenter testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this pattern when you need to:
*.test.ts files in this codebase.setup() functions instead of mutable beforeEach setup.@ts-expect-error.Load these on demand based on what you're working on:
@ts-expect-error, bun:test type strategy, no as any), read references/type-testing.mdsetup() patterns, composable setup, beforeEach avoidance, shared schemas), read references/setup-pattern.mddescribe() boundaries, helper-over-nesting), read references/test-structure.mdExternal reading:
expectTypeOf for type testingshoehorn — partial mocks for test ergonomicsRelated Skills: See
services-layerfor the service patterns being tested. Seetypescriptfor type testing conventions.
Every .test.ts file MUST start with a JSDoc block explaining what is being tested and the key behaviors verified. This serves as documentation for the module's contract.
/**
* [Module Name] Tests
*
* [1-3 sentences explaining what this file tests and why these tests matter.]
*
* Key behaviors:
* - [Behavior 1]
* - [Behavior 2]
* - [Behavior 3]
*
* See also:
* - `related-file.test.ts` for [related aspect]
*/
/**
* Cell-Level LWW CRDT Sync Tests
*
* Verifies cell-level LWW conflict resolution where each field
* has its own timestamp. Unlike row-level LWW, concurrent edits to
* DIFFERENT fields merge independently.
*
* Key behaviors:
* - Concurrent edits to SAME field: latest timestamp wins
* - Concurrent edits to DIFFERENT fields: BOTH preserved (merge)
* - Delete removes all cells for a row
*/
// Tests for create-tables
For long test files (100+ lines), use comment headers to separate logical sections:
// ============================================================================
// MESSAGE_SYNC Tests
// ============================================================================
When a module has distinct behavioral aspects, split into focused test files rather than one monolithic file:
| Pattern | Use Case |
| ----------------------------- | ---------------------------------------------------- |
| {module}.test.ts | Core CRUD behavior, happy paths, edge cases |
| {module}.types.test.ts | Type inference verification, negative type tests |
| {module}.{scenario}.test.ts | Specific scenarios (CRDT sync, offline, integration) |
Test descriptions MUST be behavior assertions, not vague descriptions. The name should tell you what broke when the test fails.
test('upsert stores row and get retrieves it', () => { ... });
test('filter returns only published posts', () => { ... });
test('concurrent edits to different fields: both preserved', () => { ... });
test('delete vs update race: update wins (rightmost entry)', () => { ... });
test('observer fires once per transaction, not per operation', () => { ... });
test('get() throws for undefined tables with helpful message', () => { ... });
test('should work correctly', () => { ... }); // What works? What's correct?
test('should handle batch operations', () => { ... }); // Handle how?
test('basic test', () => { ... }); // Says nothing
test('should create and retrieve rows correctly', () => { ... }); // Vague "correctly"
{action} {outcome} [condition]"upsert stores row and get retrieves it"
^^^^^^ ^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^
action outcome action outcome
"observer fires once per transaction, not per operation"
^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
subject outcome condition
"get() returns not_found for non-existent rows"
^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
action outcome condition
documentation
Yjs CRDT patterns, shared types (Y.Map, Y.Array, Y.Text), conflict resolution, and document storage. Use when the user mentions Yjs, Y.Doc, CRDTs, collaborative editing, or when handling shared types, implementing real-time sync, or optimizing document storage.
tools
Voice and tone rules for all written content—prose, UI text, tooltips, error messages. Use when the user says "fix the tone", "rewrite this", "sounds like AI", "sounds corporate", or when writing any user-facing text, landing pages, product copy, or open-source documentation.
tools
Workspace API patterns for defineTable, defineKv, versioning, migrations, data access (CRUD + observation), withActions, and extension ordering. Use when the user mentions workspace, defineTable, defineKv, createWorkspace, withActions, withExtension, defineQuery, defineMutation, connectWorkspace, or when defining schemas, reading/writing table data, observing changes, writing migrations, chaining extensions, or attaching actions to a workspace client.
documentation
Standard workflow for implementing features with specs and planning documents. Use when the user says "start a new feature", "how should I plan this", "what's the process", or when starting implementation, planning work, or working on any non-trivial task.