Instruction file imported from vishiri/fantasia-archive (
.cursor/rules/vitest-tests.mdc). Copyright stays with the author.
Vitest tests (*.vitest.test.ts)
Running and reports
- Fast unit pass (no coverage):
yarn test:unit— Vitest multi-project root (testing-terminal-isolation.mdc):unit-electron,unit-src-renderer,unit-helpers,unit-i18n,unit-components. Rootroot= repository; each projectextendsfile undervitest/— globs resolve from repo root. - Dev edits (agents): connected
yarn vitest run <spec paths>only — dev-scoped-verify.mdc, fantasia-dev-scoped-verify. Commit / final cleanup / CI: fullyarn testbatch:verifybelow. - Coverage terminal reporting:
vitestTerminalReportersin vitest.reporters.shared.ts —agentfirst (omits noisy per-pass lines; failures + summary print), thenjson→test-results/vitest-report/test-results-vitest-*.json(peroutputFilein eachvitest/*.config.mts). - Full quality gate / coverage:
yarn testbatch:verifyendsyarn test:coverage:verify— v8 coverage order:yarn test:coverage:electron(95% all foursrc-electron, vitest.electron.config.mts),yarn test:coverage:helpers(95% all fourhelpers/**/*.tsunit-helpersinstruments —helpers/playwrightHelpers_*excluded per vitest.helpers.config.mts; project may run zero tests until helper packages ship),yarn test:coverage:i18n(95% scopedi18n/per vitest.i18n.config.mts),yarn test:coverage:src= vitest.src-renderer.config.mts (95%src/boot, scripts, stores) then vitest.components.config.mts (95%src/components/**/*.ts,src/layouts/**/*.ts,src/pages/**/*.ts, and matching.vue— each file viaperFile: true;src/components/foundation/**excluded frominclude). Vitest coverage tiers (CI) below = same rules numbered. Same chain asyarn test:unit:coverage. - Debug single project:
--config vitest/vitest.<slice>.config.mtsoryarn test:coverage:electron,helpers,i18n,src. Barevitest run --coveragewithout--configmixes workspace projects — prefer explicit scripts for coverage gates. - JSON reports:
test-results/vitest-report/test-results-vitest-electron.json,...-src-renderer.json,...-helpers.json,...-i18n.json,...-components.json.
Vitest coverage tiers (CI)
Canonical summary; numbers + include enforced in vitest/*.config.mts. Also README.md (Quality gates), AGENTS.md (Testing).
src-electron— 95% statements, branches, functions, lines (vitest.electron.config.mts).helpers/**/*.tsunit-helperscoverageincludematches (helpers/playwrightHelpers_*excluded — Playwright harness; correctnessyarn test:components/yarn test:e2eafter build) — 95% all four when instrumented set non-empty (vitest.helpers.config.mts). Workspace tooling undervitest/not underhelpers/.- Repo-root i18n/ entry .ts in vitest.i18n.config.mts (registry index.ts, per-locale index.ts, externalFileLoader.ts, specialCharactersFixer.ts) — 95% all four. Pure L_*.ts via indexes, not separately gated.
srcrenderer.tsunit-src-renderer— 95% all four (vitest.src-renderer.config.mts).src/components/**/*.ts,src/layouts/**/*.ts,src/pages/**/*.ts, and matching.vueunder those treesunit-components— each file 95% statements/lines, 80% branches, 100% functions (perFile: trueon threshold globs; vitest.components.config.mts).src/components/foundation/**omitted fromunit-componentscoverageinclude.
Workspace layout: vitest.config.mts root = repository; each project extends one file under vitest/ — globs e.g. src-electron/** from repo root.
Vitest 5: clearMocks defaults true. unit-components already true; other unit projects set clearMocks: false so import-time mock call counts survive. Coverage include path with no glob wildcard = directory, not file — i18n entries use **/i18n/.... Glob threshold objects must set perFile: true themselves (no inherit). Ignore .vitest/.
Placement and discovery
- Connected suites: Change production module → search other
*.vitest.test.tsimporting/mocking it or asserting samei18nkeys/domain ids →yarn vitest runevery implicated path — not only suite beside edited file. Pair Playwright per fantasia-testing Connected tests for any feature change. - Files match
**/*vitest.@(spec|test).?(c|m)[jt]s?(x)— repo uses*.vitest.test.ts. - Co-locate under
_tests/next to code: i18n/_tests, src/scripts/_utilities/_tests, src/stores/_tests, src-electron/contentBridgeAPIs/_tests, src-electron/mainScripts/_tests orsrc-electron/mainScripts/<area>/_tests/, futurehelpers/<nonPlaywrightPackage>/_tests/(unit-helpersexcludeshelpers/playwrightHelpers_*), src/components/elements/FantasiaMascotImage/_tests, src/layouts/_tests, src/pages/_tests, src/components/dialogs/DialogAppSettings/scripts/_tests. - New non-Playwright
helpers/<name>/or new.tsin such package → add/extend*.vitest.test.tssame change when non-trivial branching/I/O/public API (same assrc-electron,src/scripts). No*.vitest.test.tsunderhelpers/playwrightHelpers_*. src/,src-electron/, Vue SFCs undersrc/components/,src/layouts/,src/pages/= Vitest surfaces when mounting appropriate; full UI flows in Playwright.src/components/**,src/layouts/**,src/pages/**: 1:1 baseline — every feature.vuehas_tests/<ComponentOrLayoutOrPageName>.vitest.test.ts. Add/rename/remove SFC → update matching Vitest same change. Extractedscripts/*.ts: focusedscripts/_tests/*.vitest.test.tswhen testable logic (stores, transforms, toggles) — not every one-line wrapper. Mergescripts/*.ts→ merge/retire redundantscripts/_tests/*.vitest.test.ts(code-size-decomposition.mdc Module count).- Test file names vs SUT:
_tests/<SameBaseNameAsProduction>.vitest.test.tsnext to single*.tsor.vue. Piniaindex.ts:_tests/index.vitest.test.ts. Several Vitest files per module (different concerns): dotted suffix on production basename (e.g.dialogManagement.open.vitest.test.ts,faKeybindsGlobalDispatch.getFaKeybindKeydownContext.vitest.test.ts). Cross-module integration: umbrella name (e.g.faKeybinds.integration.vitest.test.ts) + short file-top comment listing modules exercised.
Vue and renderer test boundaries
- Vitest for deterministic renderer logic in
src/(helpers, stores/composables, serializable transforms, simple component-facing logic in TS). - SFC behavior without fragile DOM → focused unit tests on extracted logic path.
unit-componentsmust enforce 95% on colocatedscripts/*.ts+.vuebranch hard frommountalone (debouncedv-model, Pinia routing predicates,typeof documentguards, equality gates inwatch) → extract small pure helper orvi.mock-friendly resolver in featurescripts/*.ts, cover fromscripts/_tests/*.vitest.test.ts, SFC wiring only. Still extend_tests/<Feature>.vitest.test.tsmounts when integrated behavior user-visible (menus, dialogs, floating windows).- Full interaction/visual/integration → Playwright (
*.playwright.test.ts/*.playwright.spec.ts). - 1:1 mapping = every feature
.vuehas_tests/<Name>.vitest.test.ts; mounts must exercise SFC script/template handlers so each.vuemeets the same per-file thresholds as colocated.ts(vitest.components.config.mts). _data/production feeds only (menus, button lists, locale-backed blobs app ships). No Vitest fixtures there.- All Vitest fixture data inside same
*.vitest.test.ts: inlineconst/ object literals at module scope or above test. No sibling_tests/*.tsdata-only files. No_tests/_data/. - No automated tests aimed only at
_data/(src/components/**/_data/**). Cover production_dataindirectly through components/scripts importing it. - Mounted DOM in component Vitest: same
data-test-locator/data-test-*as production + Playwright (e.g.w.get('[data-test-locator="…"]')); no baredata-test.
Imports
- From
vitest: only needed —test,expect,vi,expectTypeOf(tweaks.vitest.test.ts, fixAppName.vitest.test.ts). - SUT: relative import from parent, e.g.
import { appDetailsAPI } from '../appDetailsAPI'. - JSON:
import packageJSON from '../../../package.json' assert { type: 'json' }when comparing package metadata (fixAppName.vitest.test.ts). - Node / app paths: e.g.
import os from 'os'when test mirrors runtime.
Documenting each test block
JSDoc immediately above test / test.skip (eslint-typescript.mdc: no Markdown emphasis; single quotes not grave accents). No mid-sentence breaks across * lines; full lines or * - bullets (AGENTS.md code comments).
- First line: function, API export, or concern name (e.g.
determineAppName,checkIfExternal,startApp). - Following lines: scenario, expected behavior, skip reason — complete line or bullet, not wrapped continuation mid-clause.
Examples: faExternalLinksManagementAPI.vitest.test.ts; skipped Electron-only appManagement.vitest.test.ts.
test titles
- Single sentence-style string:
Test .../Test if .../Test that .../Test for ...— match existing files.
Runnable tests
expect(...),expect(...).toBeTypeOf('string'),toContain, etc. (appDetailsAPI.vitest.test.ts, extraEnvVariables.vitest.test.ts).vi.stubEnvfor env-dependent behavior (fixAppName.vitest.test.ts). Vitest 3+:DEV,PROD,SSR→ boolean (e.g.vi.stubEnv('DEV', true)); other keysstring | undefined.src-electronimports typecheck undertsc: preferapp/...alias (eslint-typescript.mdc).vi.mock(...)when mocking modules.expectTypeOffor type-level consistency (tweaks.vitest.test.ts).- No
anyin tests/fixtures; concrete fixture types, generic helpers, orunknown+ narrowing.
Skipped tests (test.skip)
- Untestable in Vitest →
test.skip+ same JSDoc + clear title.
File-level notes
- Optional multi-line block comment at file top for scope (e.g. "Nothing else to test here") — extraEnvVariables.vitest.test.ts.
Do not
- No
describe()unless rest of suite adopts — current suites: flattest/test.skiponly. - Do not rename
*.vitest.test.ts— Vitest discovery depends on it.