.cursor/skills/fantasia-testing/SKILL.md
Runs and extends Fantasia Archive tests: Vitest unit tests vs Playwright component and E2E tests, including rebuild-before-Playwright rules and file naming. Use when writing tests, debugging CI, or when the user mentions Vitest, Playwright, component tests, or e2e.
npx skillsauth add vishiri/fantasia-archive fantasia-testingInstall 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.
Match existing tests to the letter when adding or editing:
vitest-tests.mdc (**/*.vitest.test.ts)playwright-tests.mdc (**/*playwright*.ts)data-test-locator and other data-test-*, never bare data-test): vue-template-test-hooks.mdc (**/*.vue)Same rules as vitest-tests.mdc (Vitest coverage tiers (CI) section): 100% all metrics on src-electron and helpers/**/*.ts; 100% all metrics on unit-src-renderer src .ts (boot, scripts, stores); 100% on all four for scoped i18n/ entry unit-i18n; 100% on all four for merged unit-components src/components/**/*.ts, src/layouts/**/*.ts, and src/pages/**/*.ts; matching .vue SFCs have no threshold—~60% watermarks for review (src/components/foundation/** excluded from the pool). Configs live under vitest/; entry vitest.config.mts sets repo root and extends per project.
yarn test:unit runs the Vitest multi-project root in vitest.config.mts (unit-electron, unit-src-renderer, unit-helpers, unit-i18n, unit-components) without coverage. yarn testbatch:verify ends with yarn test:coverage:verify: 100% on all four v8 metrics for src-electron (vitest.electron.config.mts) and helpers (vitest.helpers.config.mts); 100% on all four for scoped i18n/ (unit-i18n); 100% on all four for unit-src-renderer src .ts (vitest.src-renderer.config.mts); 100% on all four for unit-components merged .ts globs, with .vue SFCs only watermarked (60% lower band for review—no failing gate) (vitest.components.config.mts). Use yarn test:coverage:electron, yarn test:coverage:helpers, yarn test:coverage:i18n, or yarn test:coverage:src to debug one slice.yarn test:unit while iterating; before commits run yarn testbatch:verify (testing-terminal-isolation.mdc). Do not chain unit/coverage commands with yarn quasar:build:electron or Playwright in one shell line.test-results/vitest-report/test-results-vitest-*.json per project (electron, src-renderer, helpers, i18n, components).src/ and src-electron/ (including main-process modules) with *.vitest.test.ts co-located under _tests/ folders; component mounting tests use @vue/test-utils + shared vitest.setup.ts.helpers/ packages (e.g. helpers/playwrightHelpers/): treat like other testable modules—add or extend _tests/*.vitest.test.ts when you add or change non-trivial helper code. Vitest project unit-helpers includes only helpers/**/*.vitest.test.ts (vitest.helpers.config.mts). Keep Playwright and other harness code under repo-root helpers/<package>/; do not add harness-only packages under src/ (see AGENTS.md repository layout).src/components/**, src/layouts/**, and src/pages/**, maintain one colocated _tests/<Name>.vitest.test.ts per feature .vue (add/rename/remove both together). Extracted scripts/*.ts next to a component SFC should gain scripts/_tests/*.vitest.test.ts when they hold real logic (same idea as src/scripts helpers). When several scripts/*.ts files are merged into fewer modules for readability, consolidate the matching scripts/_tests/*.vitest.test.ts files the same way (fewer files, grouped by concern) so tests stay easy to discover — see code-size-decomposition.mdc Module count: prefer logical grouping.src/scripts/** helpers, store/composable state transitions, and other deterministic src/ logic that does not require full Electron runtime wiring._data/ is for production feeds (not automated-test fixture blobs): do not add Vitest suites aimed only at _data/ paths; validate production data indirectly via components or scripts. Vitest fixtures and Playwright fixture objects (props payloads, key lists, gold values) stay inside the respective *.vitest.test.ts / *.playwright.test.ts files as inline const data — no extra _tests/*.ts files used only as fixture dumps. Do not use _tests/_data/.test / test.skip only (no describe), JSDoc above each test naming the function under test, titles like Test that ... — see src-electron/**/_tests/*.vitest.test.ts and the vitest rule above.any in test code and fixtures; use concrete interfaces, inferred literals, or unknown narrowed before assertion/use.I_ interfaces, T_ aliases) and prefer descriptive names such as I_appMenuList / T_dialogName..vue; src .ts in the Vitest coverage include lists must meet the enforced percentages (see vitest-tests.mdc). .vue rows below ~60% lines or statements in the unit-components report warrant investigation.Critical: Playwright targets a built, production Electron app. After any source change affecting what tests exercise, run quasar build -m electron (or yarn quasar:build:electron) before Playwright. Use Node.js 22.22.0+ locally (package.json engines) so Electron / native steps match CI.
Electron userData isolation: Specs set TEST_ENV to components or e2e. The main process then uses %APPDATA%/<package.json name>/playwright-user-data (this app: fantasia-archive/playwright-user-data, not fantasia-archive-dev). fixAppName.ts applies that path in Electron. playwrightIsolatedUserDataDirName.ts exports PLAYWRIGHT_ISOLATED_USER_DATA_DIR_NAME with no electron import so playwrightUserDataReset.ts (and Vitest for path logic) can run in Node without importing fixAppName—otherwise Playwright test collection hits import { app } from 'electron' and fails. Component and E2E specs group tests in test.describe.serial; each group’s test.beforeAll calls resetFaPlaywrightIsolatedUserData() before that group’s electron.launch (do not use test.beforeEach reset while the same ElectronApplication stays open—the on-disk profile would diverge from the running process). Shared Playwright-only modules live under helpers/playwrightHelpers/; add future cross-cutting harness packages as siblings under helpers/. Do not use test.describe.parallel for these Electron specs unless the user explicitly asks; do not commit test.describe.serial.only or other .only hooks except for short local debugging.
.storybook-workspace/; config must keep staticDirs (and related Vite public/ wiring) in sync with the Quasar app so public assets resolve the same way in Storybook dev and yarn storybook:build output. See .storybook-workspace/.storybook/main.ts. Static build lands in .storybook-workspace/storybook-static/. Workspace storybook:build and test:storybook:smoke use --quiet --loglevel warn; production viteFinal further lowers Vite noise (warnings still show). Storybook VRT uses .storybook-workspace/playwright.storybook-visual.config.ts and .storybook-workspace/visual-tests/; root yarn test:storybook:visual* chains yarn storybook:build with yarn --cwd .storybook-workspace test:storybook:visual*. Per-story [storybook-visual] progress logs are off by default; set FA_STORYBOOK_VISUAL_VERBOSE=1 when debugging. @playwright/test, playwright, and http-server for that flow are devDependencies of .storybook-workspace (install with yarn --cwd .storybook-workspace install).file:// — Packaged renderer paths are not a web origin at /. If import.meta.env.BASE_URL is '/' or empty, building public/ URLs as /images/... can fail loading; use a relative prefix (e.g. ./) for those assets (see SocialContactSingleButton.vue).yarn quasar:build:electron before yarn test:components / yarn test:e2e when exercised sources changed; flaky UI in tests after a green Storybook pass often means a stale build or a file:// URL mismatch.layouts-componenttestinglayout--with-social-contact-single-button and pages-componenttesting--social-contact-single-button excluded from snapshot collection; they are Playwright harness utility previews and are not meaningful VRT surfaces (see EXCLUDED_STORY_IDS in .storybook-workspace/visual-tests/storybook.visual.playwright.test.ts). The GlobalLanguageSelector story uses skip-visual as well: VRT’s static iframe often omits the fixed title-bar control from captures even though yarn storybook:run shows it for manual review. For other stories, an empty iframe root after the render wait is a failure unless the story opts out with tags: ['skip-visual-render-check'] (rare) or its id is added to EXCLUDED_STORY_IDS with an explicit reason.maxDiffPixels — storybook.visual.playwright.test.ts passes maxDiffPixels into toHaveScreenshot. That value is the maximum count of differing pixels over the full screenshot (after Playwright’s per-pixel threshold), not a width. Large iframes mean even ~2000 differing pixels is a small fraction of the image; the cap mainly absorbs font / subpixel / Chromium differences between a developer Windows machine and GitHub Actions windows-latest when comparing the same committed -chromium-win32.png baseline. If yarn test:storybook:visual passes locally but yarn testbatch:ensure:nochange fails on GitHub with diffs slightly over the cap, check CI’s reported pixel counts and diff PNGs—then either bump maxDiffPixels with a comment citing observed CI counts, or run yarn test:storybook:visual:update when UI changes are intentional. See README Storybook visual baseline policy and storybook-stories.mdc.yarn testbatch:ensure:nochange runs yarn testbatch:verify + yarn quasar:build:electron:summarized + yarn test:components + yarn test:e2e + yarn test:storybook:smoke + yarn test:storybook:visual (committed snapshot compare). yarn testbatch:ensure:change is the same through smoke, then yarn test:storybook:visual:update for intentional baseline refresh only.playwright.config.ts)outputDir: test-results/playwright-artifacts (per-test subfolders; Playwright copies testInfo.attach path-based files into each test's attachments/). testMatch limits runs to src/components/** and e2e-tests/**. Terminal reporter line (concise progress; failures in full; no list of every passing test). HTML report: test-results/playwright-report (attachment bytes are duplicated into playwright-report/data/). yarn test:components / yarn test:e2e (and single-spec variants) run via scripts/playwrightWithArtifactTrim.mjs, which deletes test-results/playwright-artifacts after the run so only the HTML report tree (including data/*.webm) remains. Raw recordVideo output uses an OS temp dir and is removed after attach (helpers/playwrightHelpers/playwrightElectronRecordVideo.ts).workers: 1, fullyParallel: false — assume sequential, single-worker runs unless you change config.test.describe.serial: in test.beforeAll, pass testInfo to getFaPlaywrightElectronRecordVideoPartial(testInfo) for electron.launch, then in test.afterAll(async ({}, afterAllTestInfo) => { ... }) call closeFaElectronAppWithRecordedVideoAttachments(electronApp, suiteTestInfo, afterAllTestInfo) instead of raw electronApp.close() so the HTML report shows WebM on the suite’s last test when the group has multiple tests. After const appWindow = await electronApp.firstWindow(), call installFaPlaywrightCursorMarkerIfVideoEnabled(appWindow) from helpers/playwrightHelpers/playwrightElectronRecordVideo.ts. Set FA_PLAYWRIGHT_CURSOR_MARKER to '0' or 'false' to disable the dot; set FA_PLAYWRIGHT_NO_VIDEO to '1' or 'true' to skip recording. Recordings use 1920×1080 via recordVideo.size.test.describe.serial group’s electron.launch can produce a usable WebM recording attached via that suite’s TestInfo. After yarn test:components, yarn test:e2e, or the :single / :single:ci variants, open test-results/playwright-report/index.html in a browser, drill into a test row, and use Attachments to play or save the video. Files the UI plays from live under test-results/playwright-report/data/ (content-addressed names).test-results/playwright-report/. Running a different suite (component vs E2E) or re-running the same suite replaces the previous report—nothing is accumulated there. The yarn Playwright scripts also remove test-results/playwright-artifacts after each run via scripts/playwrightWithArtifactTrim.mjs so duplicate on-disk copies are not kept beside the report.test-results/playwright-report/index.html (or to share a screenshot or textual failure) rather than treating raw .webm blobs as inspectable prose.Structure: Match imports, header constants (extraEnvSettings, electronMainFilePath, faFrontendRenderTimer, selectorList), wrap cases in test.describe.serial, beforeAll (resetFaPlaywrightIsolatedUserData, launch, firstWindow, cursor marker, render wait), afterAll (closeFaElectronAppWithRecordedVideoAttachments), JSDoc per test, and shared appWindow in each test body — see .cursor/rules/playwright-tests.mdc and any existing test beside the component.
Locators: Prefer data-test-locator plus other data-test-* from .vue templates (never bare data-test); document static values in selectorList and use small helpers beside it for dynamic data-test-locator suffixes (see SocialContactSingleButton.playwright.test.ts vs DialogProgramSettings.playwright.test.ts programSettingsSelector). Exception locators (Quasar portaled [role="tooltip"], E2E menu strings, etc.) still belong in selectorList as the full selector string under a clear key (for example quasarTooltip: '[role="tooltip"]') — avoid raw literals in the test body. For suites with many tooltips, put duplicate tooltip text on the trigger (data-test-tooltip-text) for bulk string checks and keep at least one hover + live tooltip assertion (appWindow.locator(selectorList.quasarTooltip), etc.) so behavior stays real (DialogProgramSettings.vue).
Layout width/height: Prefer data-test-layout-width, data-test-layout-height, and domain caps such as data-test-error-card-width (wired from the same props or markup as sizing) so component and E2E specs stay stable when the window or parent is narrow. Use locator.boundingBox() only when you need rendered pixels and the harness guarantees enough space, or for inequality checks. See the Layout size (width and height) section in playwright-tests.mdc.
Typing: Keep selectors, props payloads, and helper arguments strongly typed; avoid any.
Command: yarn test:components
Execution policy: run this command in its own terminal invocation; never combine it with other verification commands in one chained shell command.
Location: Under src/components/, files ending in .playwright.test.ts (often in a _tests/ subfolder next to the component).
Single test: yarn test:components:single --component=<bucket>/<ComponentName> (for example dialogs/DialogProgramSettings, elements/ErrorCard; see package.json for Windows %npm_config_*% variants). src/components/foundation/** does not ship Playwright specs (Storybook-only catalogues).
Interactive picker: yarn test:components:list → runs testRunner_component.mjs (discovers *.playwright.test.ts under src/components/, lists choices as bucket/ComponentFolder matching the repo tree).
Structure: Same test.describe.serial / beforeAll / afterAll pattern as components; e2e files use TEST_ENV: 'e2e', may use numeric faFrontendRenderTimer, and sometimes getByText with visible labels — see e2e-tests/*.playwright.spec.ts in the repo.
Command: yarn test:e2e
Execution policy: run this command in its own terminal invocation; keep E2E output isolated from other command logs.
Location: e2e-tests/*.playwright.spec.ts
Single spec: yarn test:e2e:single --spec=SPEC_FILE_NAME (see package.json).
Interactive picker: yarn test:e2e:list → testRunner_e2e.mjs
yarn testbatch:ensure:nochange runs yarn testbatch:verify (lint + types + stylelint + Vitest coverage with layered gates per vitest-tests.mdc) + yarn quasar:build:electron:summarized + Playwright component + Playwright E2E + Storybook smoke + Storybook visual compare in one chain. yarn testbatch:ensure:change ends with Storybook visual snapshot update instead of compare — use only when baselines should change (see testing-terminal-isolation.mdc for when to split commands across terminals instead).yarn testbatch:verify — fix issues per eslint-typescript.mdc and vitest-tests.mdc (testing-terminal-isolation.mdc).yarn quasar:build:electron (or quasar build -m electron) — its own terminal.yarn test:components / yarn test:e2e as needed — each in its own terminal; do not chain with yarn quasar:build:electron or with each other in one line, unless you intentionally run yarn testbatch:ensure:nochange or yarn testbatch:ensure:change.yarn test:storybook:smoke and yarn test:storybook:visual (or use yarn testbatch:ensure:nochange to cover verify + build + Playwright + Storybook in one shot). Use yarn testbatch:ensure:change only when deliberately updating committed Storybook snapshots.src/ and keep assertions deterministic.yarn storybook:run (interactive) and yarn test:storybook:smoke / storybook dev --smoke-test --ci (startup verification).yarn storybook:build, run yarn test:storybook:visual from the repo root, or yarn --cwd .storybook-workspace test:storybook:visual if storybook-static/ is already present. Update baselines with yarn test:storybook:visual:update (or workspace test:storybook:visual:update). HTML/report output and artifacts stay under repo-root test-results/storybook-visual-* locally (not run in GitHub Actions; use yarn testbatch:ensure:nochange or the individual scripts when you need the full gate). Screenshot drift surfaces Expected / Actual / Diff on each failing per-story test.step in test-results/storybook-visual-report/index.html (soft assertions keep the run walking the story list)._tests/ subfolders as src/components/**/_tests/<Component>.stories.ts, with meta.title Components/<bucket>/<ComponentName> (same dialogs / elements / foundation / globals / other buckets as src/components/). foundation/ catalogues are Storybook-only (no Playwright specs); see AGENTS.md Foundation components.src/layouts/** and src/pages/** stories (if present) live in _tests/ subfolders (src/layouts/**/_tests/*.stories.ts, src/pages/**/_tests/*.stories.ts) and are canvas-only previews; do not expect or add Storybook Docs/autodocs for them (see storybook-stories.mdc).L_* locale module imports plus placeholder documents.* strings to prevent markdown import-analysis failures.types/)interface / type declarations in repository-root types/ (import with app/types/...). Prefer one domain-oriented module per feature area with brief JSDoc on exports (see types/I_appMenusDataList.ts). Do not add colocated <filename>.types.ts under src/, src-electron/, or .storybook-workspace/. Ambient augmentations for third-party modules also live under types/ and are loaded with a side-effect import from the owning boot file or src/stores/index.ts (see types/piniaModuleAugmentation.ts)..js), TypeScript (.ts), Vue (.vue), and JSON (.json, .jsonc, .json5) files, enforce expanded multi-line object literals via ESLint (object-curly-newline + object-property-newline) and keep files auto-fixable with eslint --fix.development
Splits working tree changes into logical Git commits with conventional messages (feat, fix, test, chore, refactor, style, docs). Proposes commits one at a time and waits for explicit user approval before each git commit. Use when the user asks to commit, split commits, stage by topic, or use conventional / semantic commit messages.
development
Aligns AI suggestions with Fantasia Archive as a worldbuilding database manager: projects, documents, and in-app concepts. Use when designing UX, data models, menus, or copy that should match the product purpose.
development
Designs SQLite usage in Fantasia Archive’s Electron main process: file locations under userData, native better-sqlite3 module constraints, and migrations. Use when editing electron-main database code, schema, or persistence paths.
development
Produces production Electron builds for Fantasia Archive with electron- builder and Quasar. Use when changing build scripts, packaging, or publish flags, or when the user asks how to ship the desktop app.