.agents/skills/repo-source-code-test/SKILL.md
Write unit tests for Formisch packages/core with proper TypeScript types. Use when creating new tests, fixing type errors in tests, or adding test coverage for core functions.
npx skillsauth add open-circle/formisch repo-source-code-testInstall 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.
Guide for writing high-quality unit tests in packages/core/ with proper TypeScript types.
as assertionsif blocks to narrow types only when TypeScript reports an errordocument.createElement(), not mock objectsTests live next to their implementation:
packages/core/src/
├── form/
│ └── validateFormInput/
│ ├── validateFormInput.ts
│ └── validateFormInput.test.ts
├── field/
│ └── getFieldInput/
│ ├── getFieldInput.ts
│ └── getFieldInput.test.ts
The global setup file (src/vitest/setup.ts) automatically calls mockFramework() and sets up beforeEach with resetIdCounter(). Use the shared createTestStore helper:
import * as v from 'valibot';
import { describe, expect, test } from 'vitest';
import { createTestStore } from '../../vitest/index.ts';
describe('functionName', () => {
test('should do something', () => {
const store = createTestStore(v.object({ name: v.string() }));
// Test logic
});
test('should handle initial input', () => {
const store = createTestStore(v.object({ name: v.string() }), {
initialInput: { name: 'John' },
});
// Test logic
});
});
The createTestStore helper accepts a schema and an optional config object:
createTestStore(schema, {
initialInput?: unknown, // Initial form values
validate?: ValidationMode, // 'initial' | 'touch' | 'input' | 'change' | 'blur' | 'submit'
revalidate?: ValidationMode, // Same options except 'initial'
issues?: [...], // Mock validation issues
});
For tests that need DOM APIs (focus, createElement, etc.), add the directive:
// @vitest-environment jsdom
import { describe, expect, test } from 'vitest';
InternalFieldStore is a union type:
type InternalFieldStore =
| InternalArrayStore // has: kind: 'array', children: InternalFieldStore[]
| InternalObjectStore // has: kind: 'object', children: Record<string, InternalFieldStore>
| InternalValueStore; // has: kind: 'value', NO children property
When to use type guards: Only use if blocks for type narrowing when TypeScript reports an error.
❌ Bad — Type cast:
expect(store.children.items.children[0].input.value).toBe('a');
// Error: Property 'children' does not exist on type 'InternalFieldStore'
// Wrong fix:
expect(
(store.children.items as InternalArrayStore).children[0].input.value
).toBe('a');
✅ Good — Type guard with assertion:
const itemsStore = store.children.items;
expect(itemsStore.kind).toBe('array');
if (itemsStore.kind === 'array') {
expect(itemsStore.children[0].input.value).toBe('a');
}
The pattern:
const itemsStore = store.children.items;expect(itemsStore.kind).toBe('array'); (test fails if wrong)if (itemsStore.kind === 'array') { ... } (TypeScript narrows type)import type {
InternalArrayStore,
InternalFormStore,
InternalObjectStore,
} from '../../types/index.ts';
test('should initialize array schema', () => {
const store = createTestStore(v.object({ items: v.array(v.string()) }), {
initialInput: { items: ['a', 'b'] },
});
const itemsStore = store.children.items;
expect(itemsStore.kind).toBe('array');
if (itemsStore.kind === 'array') {
expect(itemsStore.children).toHaveLength(2);
expect(itemsStore.children[0].input.value).toBe('a');
expect(itemsStore.children[1].input.value).toBe('b');
}
});
const mockFocus = vi.fn();
store.children.name.elements = [{ focus: mockFocus } as HTMLElement];
// Error: Type '{ focus: Mock }' is not assignable to type 'FieldElement'
const inputElement = document.createElement('input');
const mockFocus = vi.spyOn(inputElement, 'focus');
store.children.name.elements = [inputElement];
await validateFormInput(store, { shouldFocus: true });
expect(mockFocus).toHaveBeenCalledOnce();
Note: Requires // @vitest-environment jsdom at file top.
When testing validation, create properly typed issue helpers:
function objectPath(key: string, value: unknown = ''): v.ObjectPathItem {
return { type: 'object', origin: 'value', input: {}, key, value };
}
function arrayPath(key: number, value: unknown = ''): v.ArrayPathItem {
return { type: 'array', origin: 'value', input: [], key, value };
}
function validationIssue(
message: string,
path?: [v.IssuePathItem, ...v.IssuePathItem[]]
): v.BaseIssue<unknown> {
return {
kind: 'validation',
type: 'check',
input: '',
expected: null,
received: 'unknown',
message,
path,
};
}
Note: The path type is [v.IssuePathItem, ...v.IssuePathItem[]] (tuple with at least one item).
Property 'children' does not exist on type 'InternalFieldStore'.
Fix: Use type guard pattern (see above).
Type '{ focus: Mock }' is not assignable to type 'FieldElement'.
Fix: Use real DOM elements with document.createElement().
Type 'IssuePathItem[]' is not assignable to type '[IssuePathItem, ...IssuePathItem[]]'.
Fix: Use tuple type [v.IssuePathItem, ...v.IssuePathItem[]] for path arrays.
Group tests by functionality:
describe('functionName', () => {
describe('basic behavior', () => {
test('should handle simple case', () => {});
});
describe('error handling', () => {
test('should return errors for invalid input', () => {});
});
describe('nested fields', () => {
test('should handle nested objects', () => {});
});
});
// ✅ Good
test('should focus first error field when shouldFocus is true', () => {});
// ❌ Bad
test('focus test', () => {});
# Run all tests
pnpm -C packages/core test
# Run tests in watch mode
pnpm -C packages/core test --watch
# Run specific test file
pnpm -C packages/core test validateFormInput
# Run with coverage
pnpm -C packages/core test --coverage
Before committing tests:
as SomeType) — use type guards instead.ts extensionimport type { ... }// @vitest-environment jsdom directivedocument.createElement()expect(...).toBe(...) + if guardspnpm testpnpm lintconst fieldStore = store.children.fieldName;
expect(fieldStore.kind).toBe('array');
if (fieldStore.kind === 'array') {
expect(fieldStore.children).toHaveLength(2);
}
const input = document.createElement('input');
const spy = vi.spyOn(input, 'focus');
store.children.name.elements = [input];
validationIssue('Error message', [objectPath('field'), arrayPath(0)]);
development
Add new documentation guides and tutorials to the Formisch website. Use when creating guides about form concepts, features, or techniques that aren't covered by existing documentation.
development
Update existing API documentation when Formisch source code changes. Use when function signatures, types, interfaces, or JSDoc comments change in the library source.
development
Review and verify API documentation routes on the Formisch website. Use when checking documentation accuracy, completeness, and consistency with source code.
development
Create new API documentation routes for the Formisch website. Use when adding documentation for new exported functions, types, components, or methods that don't yet have website documentation.