Skip to main content

repo-source-code-test-frameworks

Write unit and type tests for Formisch framework packages (frameworks/angular, frameworks/preact, frameworks/solid, frameworks/svelte, frameworks/vue, frameworks/react, frameworks/react-native, frameworks/honox). Use when adding tests for hooks/composables/runes/inject functions (useForm/createForm/injectForm, useField/injectField, useFieldArray/injectFieldArray) or components and directives (Form, Field, FieldArray, formischForm, formischField) in any framework wrapper.

소스 정보

저장소
open-circle/formisch
최근 소스 활동
2026년 10월 28일 04:55
감지된 SKILL.md 언어
영어
스타
1,221
포크
40

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
repo-source-code-test-frameworks
description
Write unit and type tests for Formisch framework packages (frameworks/angular, frameworks/preact, frameworks/solid, frameworks/svelte, frameworks/vue, frameworks/react, frameworks/react-native, frameworks/honox). Use when adding tests for hooks/composables/runes/inject functions (useForm/createForm/injectForm, useField/injectField, useFieldArray/injectFieldArray) or components and directives (Form, Field, FieldArray, formischForm, formischField) in any framework wrapper.
metadata
{"author":"formisch","version":"1.0"}
# Writing Framework Tests Tests for `frameworks/<fw>/`. For `packages/core/` or `packages/methods/`, use `repo-source-code-test-packages`. ## Rules 1. **React is the canonical reference.** New shared tests land in `frameworks/react` first, then port. 2. **Cross-framework consistency.** Shared API (`useForm`/`createForm`/`injectForm`, `useField`, `useFieldArray`, `Field`, `FieldArray`) keeps the same `describe`/`test` names, schemas, `initialInput`, expected outputs. Only mechanical adapters differ. Test what the adapter actually exports: React Native has no `Form` component, and Angular ships directives instead of components. 3. **No framework hint in test names.** Never `'should ... (Solid-only)'`. Use a `// ...` comment above the test if framework-specific reasoning matters. 4. **No tautological assertions.** A test must be able to fail under a real source change. 5. **100% coverage** on non-excluded files. Match React's `vitest.config.ts` exclusions: `src/types`, `src/vitest`, `**/index.ts(x)`, test files. 6. **`vi.waitFor` for async, never `setTimeout(0)`.** ## File layout ``` frameworks/<fw>/ ├── vitest.config.ts # framework plugin + jsdom + setupFiles + coverage exclusions ├── package.json # `test: vitest run --typecheck`, framework testing-library devDep └── src/ ├── vitest/ │ ├── setup.ts # jest-dom + cleanup │ ├── renderHook.ts # (Vue/Svelte only) │ ├── renderHook.tsx # (HonoX only) │ ├── render.tsx # (HonoX only) `renderHono` mount helper │ └── *Host.svelte # (Svelte only) shared hosts ├── hooks|primitives|composables|runes|functions/<name>/<name>.test.{ts,tsx} + .test-d.ts └── components|directives/<Name>/<Name>.test.{ts,tsx} └── *Host.test.svelte # (Svelte only) per-test hosts ``` ## Standard test set Identical `describe`/`test` names across all frameworks: | Source | `describe` blocks | Tests | | --------------------------------------- | ----------------------------------------------------------------------------- | ----------- | | `useForm` / `createForm` / `injectForm` | `initialization`, `initial validation`, `reactivity` | 4 + 2 type | | `useField` / `injectField` | `initialization`, `input updates`, `validation modes`, `element registration` | 10 + 4 type | | `useFieldArray` / `injectFieldArray` | `initialization`, `reactivity` | 6 + 4 type | | `Form` / `Field` / `FieldArray` | (flat) | 3 each | `Form` only exists where the adapter provides it: React Native has no `Form` component, and Angular ships the `[formischForm]` directive instead. The `store stability` describe checks that the returned stores keep their reference across re-renders. It exists in **Preact, React, React Native and HonoX**; in Angular, Solid, Svelte and Vue omit it with a one-line `// Note: ...` comment. ## Per-framework adapters | Concern | Preact | Solid | Svelte | Vue | React | React Native | HonoX | Angular | | --------------- | ------------------------- | -------------------------- | ------------------------- | ---------------------- | ------------------------ | ------------------------ | ------------------------------------- | ---------------------------------- | | Hook name | `useForm` | `createForm` | `createForm` | `useForm` | `useForm` | `useForm` | `useForm` | `injectForm` | | Read state | `form.isValid.value` | `form.isValid` | `form.isValid` | `form.isValid` | `form.isValid` | `form.isValid` | `form.isValid` | `form.isValid()` | | Imperative set | `field.onInput(v)` | `field.onInput(v)` | `field.onInput(v)` | `field.input = v` | `field.onChange(v)` | `field.onChange(v)` | `field.onChange(v)` | `field.setInput(v)` | | DOM input event | `fireEvent.input` | `fireEvent.input` | `fireEvent.input` | `input.setValue(v)` | `fireEvent.change` | `fireEvent.change` | `fireEvent.input` | `input.dispatchEvent(new Event())` | | Auto-focus prop | `autofocus` | `autofocus` | `autofocus` | `autofocus` | `autoFocus` | none | `autofocus` | none | | Field children | function | function | `{#snippet children}` | `v-slot` / scoped slot | function | function | function | `*formischField` + `let field` | | Testing library | `@testing-library/preact` | `@solidjs/testing-library` | `@testing-library/svelte` | `@vue/test-utils` | `@testing-library/react` | `@testing-library/react` | `@testing-library/dom` + `renderHono` | `@angular/core/testing` (TestBed) | ## `renderHook` - **Preact / React / React Native:** from the testing library — `{ result: { current }, rerender, unmount }`. - **HonoX:** custom helper at `src/vitest/renderHook.tsx` with the same `{ result: { current }, rerender, unmount }` shape, as there is no hono/jsx testing library. - **Solid:** from `@solidjs/testing-library` — `{ result, owner, cleanup }`. `result` is direct, not wrapped in `.current`. Use `createRoot` from `solid-js` only if no JSX context is needed; our primitives need an owner so `renderHook` is the default. - **Vue:** custom helper at `src/vitest/renderHook.ts` — mounts a `defineComponent({ setup() { return hook(); } })` (the canonical Vue Test Utils [TestComponent recipe](https://test-utils.vuejs.org/guide/advanced/reusability-composition)). Required because `onBeforeMount`/`computed` need component context. - **Angular:** no `renderHook`. Call the inject functions inside `TestBed.runInInjectionContext(() => …)` (or `runInInjectionContext(injector, …)` when you need to control destruction), and mount a host `@Component` with `TestBed.createComponent` for directive tests. - **Svelte:** custom helper at `src/vitest/renderHook.ts` — mounts `Hook.svelte`. Required because `onMount` and `[createAttachmentKey()]` need template context. For pure rune logic with no lifecycle, `$effect.root(() => { ... })()` in a `.test.svelte.ts` is lighter — but no covered rune in this repo qualifies. ## Async ```ts // ❌ flaky await new Promise((r) => setTimeout(r, 0)); // ✅ await vi.waitFor(() => expect(form.isValid).toBe(false)); ``` For Svelte rune updates, `flushSync()` from `svelte` synchronously drains derived/effect updates before the assertion. For Vue, `flushPromises()` from `@vue/test-utils` is fine for one-shot microtask drains; prefer `vi.waitFor` for multi-tick reactive flows. ## Type tests (`*.test-d.ts`) ```ts import * as v from 'valibot'; import { describe, expectTypeOf, test } from 'vitest'; import type { FieldStore } from '../../types/index.ts'; import { useForm } from '../useForm/index.ts'; import { useField } from './useField.ts'; describe('useField', () => { test('should narrow input type for primitive leaves', () => { const schema = v.object({ name: v.string() }); const form = useForm({ schema }); expectTypeOf(useField(form, { path: ['name'] }).input).toEqualTypeOf< string | undefined >(); }); test('should reject invalid paths', () => { const form = useForm({ schema: v.object({ name: v.string() }) }); // @ts-expect-error nonexistent field useField(form, { path: ['nonexistent'] }); }); }); ``` For Preact, wrap leaf types in `ReadonlySignal<...>`, and for Angular in its `Signal<...>` — those are the only framework-specific deltas in `.test-d.ts` files. Angular's `types/field.test-d.ts` additionally checks that a `FieldStore` stays assignable across schema and path generics, which is what its directives rely on. ## Schema types Always derive with `typeof schema`. If the schema is referenced only at the type level (host owns runtime), suppress the lint warning rather than hand-writing the valibot generic shape: ```ts // eslint-disable-next-line @typescript-eslint/no-unused-vars -- referenced only via `typeof schema` const schema = v.object({ name: v.string() }); type Schema = typeof schema; ``` ## Framework gotchas **Angular** — every reactive `field.*` / `form.*` is an Angular `Signal<T>`; read by calling it (`field.isDirty()`). Setter is `field.setInput(v)`. There is no `props` object: templates bind `[formischControl]="field"` and pass the value with `[value]="field.input() ?? ''"`. Field tests mount a host `@Component` whose template uses `*formischField="['path'] of form; let field"`, configure `TestBed` with `provideZonelessChangeDetection()`, and drive the DOM with `input.dispatchEvent(new Event('input'))` followed by `await fixture.whenStable()` and `fixture.detectChanges()`. `src/vitest/setup.ts` already calls `TestBed.initTestEnvironment` and resets the module after each test. **HonoX** — there is no hono/jsx testing library, so components mount with `renderHono` from `src/vitest/render.tsx` and hooks with `renderHook` from `src/vitest/renderHook.tsx`, and assertions query the DOM with `@testing-library/dom`. Wrap state updates in `flushSync` from `hono/jsx/dom`. hono/jsx maps `onChange` to the native `input` event, so DOM tests fire `fireEvent.input`. Submit forms by clicking a submit button, as hono/jsx treats every untrusted submit event as one of its own form actions. Elements unregister through the cleanup returned by the `ref` callback, which hono/jsx calls before it detaches the element. Server-side rendering tests live in `*.ssr.test.tsx` and run in the separate Node `server` Vitest project. **Preact** — every reactive `field.*` / `form.*` is a `ReadonlySignal<T>`; read with `.value`. Setter is `field.onInput(v)`. DOM events fire on `oninput`. `props.autofocus` is lowercase. **React Native** — tests render through `react-native-web` with `@testing-library/react` and jsdom, so `fireEvent.change` / `fireEvent.focus` work on the rendered `TextInput`. `field.props` has no `name` and no `autoFocus`; text is set via `onChangeText`. There is no `Form` component and no `submit` method, so submission tests call `handleSubmit`. Element cleanup differs by React version: `react-native-web` discards ref cleanup functions, so tests that assert per-element unregistration must render raw elements — see the comments in `useField.test.tsx`. **Solid** — primitive is `createForm` (not `useForm`). Methods (`insert`, `swap`) don't need `act()`. Use `class=` not `className=` in JSX. `vite-plugin-solid` ≥ 2.8.2 auto-configures vitest; don't add manual deps overrides. **Vue** — `field.props` has no `onInput`; wire DOM input via the setter: ```ts h('input', { ...field.props, value: field.input ?? '', onInput: (e: Event) => {
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기