You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/testing-and-tooling.md

6.6 KiB

title type audience status
Testing, Validation & Codegen reference human + agent current

Testing, Validation & Codegen

The cross-cutting story of how the framework stays correct: the test suite, the contract validators, what is generated vs authored, and the SSR posture. The commands are the scripts in package.json; this groups them by what they are for.

The verification loop (the short version)

npm run check    # types — svelte-kit sync && svelte-check (expect 0 errors)
npm run test     # the vitest suite (one run)
npm run lint     # prettier --check  ·  npm run format to fix

For a component you also run the contract validators (below). A change is not done until check is clean and the relevant validators pass.

Tests — two projects

vite.config.ts defines two vitest projects (see CLAUDE.md → "Vitest Two-Project Structure"):

  • client — browser tests via Playwright, for *.svelte.{test,spec}.{js,ts}. This is where component providers are exercised with real runes + DOM.
  • server — Node environment for *.{test,spec}.{js,ts} (excludes the svelte tests). Pure engines, libs, and server logic.
npm run test:unit                              # watch mode
npm run test                                   # one run
npx vitest run src/uix/morfo/compile.test.ts   # a single file
npx vitest run -t "describe name"              # by test name

Component providers carry their own {name}-provider.svelte.test.ts under the client project; the reusable engines they consume live in $libs/datagrid, $libs/forms, $libs/strings and are tested there, not inside soma. See SOMA_ARCHITECTURE.md §6 for the per-component coverage map.

Validation — what each script catches

These catch classes of bug that check (types) and an HTTP 200 (SSR) miss.

Command Catches
npm run morfo:check the real DOM vs the morfo contract — navigates each demo and validates emitted data-* against the declaration.
npm run morfo:vocabulary canonical-verb / vocabulary drift in morfo events (e.g. open|closed, declared verbs not in SEMA_VERBS). Hard-fails CI on declared-verb drift.
npm run component:audit the acceptance matrix of COMPONENT_COMPLETION_CHECKLIST.md — per-rule severity/applicability across all four layers + recipe + demo.
npm run perm:check state-transition bugs — cycles a component through its declared states (via data-perm-step annotations) and re-validates morfo after each. Catches reactivity loops and transition-time drift morfo:check can't.
npm run smoke runtime / hydration errors — walks every +page.svelte with Playwright and surfaces pageerror, console.error, missing translation keys, Context "X" not found, and __uix_lang_missing__ markers. Needs npm run dev running. HTTP 200 is SSR only; smoke exercises client hydration.
npm run translations:check missing / malformed translation keys.
scripts/eidos-lint.ts · eidos-lint-all.ts classifies every [data-*] selector in eidos CSS as morfo-backed / eidos-only / invalid — drift between the morfo contract and the CSS. (Opt-in safety net; the architectural defense is the typed semaSelector builder — see the "Eidos drift defense" rule in CLAUDE.md.)
src/uix/contracts.test.ts (vitest, via npm run test) catalogue invariants that keep declarations / recipes coherent as the framework grows. Each fails on drift naming the offender, and excludes the active-dev-track set so it stays green for the maintained catalogue: VG-8 every morfo is as const satisfies Morfo, never : Morfo (a : Morfo annotation widens the literal so the schema can't check it); SYS-1 scope-drift a component shipping an eidos/components/{c}/ recipe declares 'eidos' in scope; A31 no per-item membership predicate (isSelected / isItemPressed / …) doing .current.includes (O(N²) — lift a Set, use .has()); A30 inputId registered with the parent Field in the constructor, not wrapped in a $effect; THEME-SYS-1 overlay z-index references the named --z-index-overlay-* scale, never a raw integer.

Codegen — what is generated vs authored

Some surfaces are produced from a source of truth, not hand-maintained. Don't edit the output; edit the source and regenerate.

Command Generates from
npm run generate:eidos-css the eidos foundation/recipe CSS, from EidosConfig.recipes. (generated/base.css is output — regenerate, don't hand-edit.)

The data-* contract is not a generated doc — it is the morfo itself, validated live by npm run morfo:check. (A legacy generate:contracts-docs script and its output DATA_ATTRS.md, both fossils of the removed terra layer, were deleted.)

The morfo compiler itself is the central codegen primitive: compileMorfo(morfo) turns a declaration into a CompiledMorfo (resolved attr / keyboard / action plans + the closed set of CSS selectors eidos may use), cached by morfo identity (WeakMap). Every consumer reads the compiled morfo, never the raw declaration.

npm run eidos:purge   # production: drop unreached foundation CSS (−46 to −55%)

SSR posture

Components are headless-functional without a DOM. The architecture keeps SSR and headless tests working:

  • dom:false injects a shared disabledDom no-op consumed by Soma / Sema / Eidos — there is no silent fallback to direct DOM writes (see arts/adom/README and src/uix/contracts.ts).
  • ActiveDom resolves the owner document / window (getDocument(node) / getWindow(node)), so it is correct under iframes, popups and happy-dom — not bound to the global document.
  • Sema is ornamental — ActiveUix.events (the perceptual engine) is optional; with no engine, SomaRuntime.trigger() skips the emit and the component stays functional. So sound/haptic-free, audio-disabled and server environments work.
  • Hydration is what HTTP 200 misses — server render succeeds long before a hydration-time Context not found or effect loop would; that is exactly what npm run smoke exists to catch.

See also

Powered by TurnKey Linux.