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). 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.

Informações da origem

Repositório
open-circle/formisch
Última atividade na origem
29 de julho de 2026 às 23:25
Idioma detectado do SKILL.md
inglês
Estrelas
1.200
Forks
36

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
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). 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) │ └── *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 tests `useMemo` across re-renders and exists only in **React and React Native**. In other frameworks omit it with a one-line `// Note: ...` comment. ## Per-framework adapters | Concern | Preact | Solid | Svelte | Vue | React | React Native | Angular | | --------------- | ------------------------- | -------------------------- | ------------------------- | ---------------------- | ------------------------ | ------------------------ | ---------------------------------- | | Hook name | `useForm` | `createForm` | `createForm` | `useForm` | `useForm` | `useForm` | `injectForm` | | Read state | `form.isValid.value` | `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.setInput(v)` | | DOM input event | `fireEvent.input` | `fireEvent.input` | `fireEvent.input` | `input.setValue(v)` | `fireEvent.change` | `fireEvent.change` | `input.dispatchEvent(new Event())` | | Auto-focus prop | `autofocus` | `autofocus` | `autofocus` | `autofocus` | `autoFocus` | none | none | | Field children | function | function | `{#snippet children}` | `v-slot` / scoped slot | 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` | `@angular/core/testing` (TestBed) | ## `renderHook` - **Preact / React / React Native:** from the testing library — `{ result: { current }, rerender, unmount }`. - **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. **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) => { field.input = (e.target as HTMLInputElement).value; }, }); ``` For "element removed" assertions, mount with `attachTo: document.body` and query `document.querySelector` — `wrapper.find` keeps finding stale nodes after `unmount()`. **Svelte** — runes need Svelte compilation. Three test patterns: - `Hook.svelte` via `renderHook` — default; required when source calls `onMount` or uses `[createAttachmentKey()]`. - `*Host.test.svelte` per-test components — for snippet/slot rendering of `Field`/`FieldArray`/`Form`. - `$effect.root` in a `*.test.svelte.ts` — only for pure rune logic with no lifecycle. Snippets can't run statements; forward values via `{@const _ = onField(field)}` with a comment explaining the pattern. After every test, `cleanup()` from `@testing-library/svelte` must run in `setup.ts` or `screen.getByTestId` finds duplicates. **Svelte build hygiene** — `package.json` `files` excludes `**/*.test.*`, `**/*.test-d.*`, `dist/vitest`. Verify after adding new fixtures: ```bash
Ver no GitHub
Este SKILL.md e muito grande, entao o SkillsMP mostra aqui apenas a primeira secao. Ver no GitHub