--- name: fantasia-testing description: >- 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. --- # Fantasia Archive — testing ## Cursor rules (detailed structure) Match **existing** tests when adding or editing: - Vitest: [`vitest-tests.mdc`](../../rules/vitest-tests.mdc) (`**/*.vitest.test.ts`) - Playwright: [`playwright-tests.mdc`](../../rules/playwright-tests.mdc) (`**/*playwright*.ts`) - Vue template hooks: [`vue-template-test-hooks.mdc`](../../rules/vue-template-test-hooks.mdc) (`**/*.vue`) ## Connected tests for any feature change Tests = **same deliverable** as production edits. 1. **Discover** — ripgrep component/dialog folder, helpers, **`data-test-locator`**, **`T_dialogName`**, **`COMPONENT_NAME`**, action/keybind ids, store symbols, **`i18n`** keys you changed. Follow imports + menu **`_data/`** entries. 2. **Vitest** — **`yarn vitest run`** with explicit paths for **every** matching **`*.vitest.test.ts`** (feature **`_tests/`**, **`scripts/_tests/`**, **`src/scripts/**/_tests`**, **`src/stores/_tests`**, **`src-electron/**/_tests`**, **`helpers/**/_tests`**, **`i18n/_tests`** when implicated). **During edits:** [fantasia-dev-scoped-verify](../fantasia-dev-scoped-verify/SKILL.md). **Before commit / final cleanup:** **`yarn testbatch:verify`**. 3. **Playwright (component)** — each matching **`src/**/_tests/*.playwright.test.ts`**: **`yarn test:components:single --component=/`** or **`yarn test:components`** in **own** terminal **after** **`yarn quasar:build:electron`** when bundle exercises changed renderer code ([testing-terminal-isolation.mdc](../../rules/testing-terminal-isolation.mdc)). 4. **Playwright (E2E)** — each matching **`e2e-tests/*.playwright.spec.ts`**: **`yarn test:e2e:single --spec=…`** or **`yarn test:e2e`** with same rebuild rule. **CI scope**: default **Verify** workflow runs **`yarn testbatch:verify`** only — not component/E2E Playwright. Run locally (or **`yarn testbatch:ensure:nochange`**) when feature touches those flows. ## Vitest coverage tiers (CI) See [vitest-tests.mdc](../../rules/vitest-tests.mdc) **Vitest coverage tiers (CI)**: **per instrumented file** (**`thresholds.perFile: true`**) — **95%** statements/lines, **80%** branches, **100%** functions on **`src-electron`**, **`helpers/**/*.ts`** (**`helpers/playwrightHelpers_*`** excluded), **`unit-src-renderer`** **`src`** **`.ts`**, scoped **`i18n/`** (**`unit-i18n`**), and **`unit-components`** **`.ts`** / **`.vue`** under **`src/components`**, **`src/layouts`**, **`src/pages`** (**`src/components/foundation/**`** excluded). Configs: [**vitest/**](../../../vitest/). ## Unit tests (Vitest) - **Commands**: **`yarn test:unit`** — multi-project root ([vitest.config.mts](../../../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`**. Debug slices: **`yarn test:coverage:electron`**, **`yarn test:coverage:helpers`**, **`yarn test:coverage:i18n`**, **`yarn test:coverage:src`**. Full tier detail: [vitest-tests.mdc](../../rules/vitest-tests.mdc). - **Execution**: **Dev edits** — [fantasia-dev-scoped-verify](../fantasia-dev-scoped-verify/SKILL.md). **Commit / final cleanup** — **`yarn testbatch:verify`**. Do not chain unit/coverage with `yarn quasar:build:electron` or Playwright in one shell line. - **Reports**: `test-results/vitest-report/test-results-vitest-*.json` per project. - **Scope**: `src/` + `src-electron/` with `*.vitest.test.ts` under `_tests/`; component mounts use `@vue/test-utils` + [vitest.setup.ts](../../../vitest/vitest.setup.ts). - **`helpers/`**: **`playwrightHelpers_*`** = Playwright harness only — extend with **`yarn test:components`** / **`yarn test:e2e`** after **`yarn quasar:build:electron`**; **no** **`*.vitest.test.ts`** there. Non-Playwright **`helpers//`**: colocate **`_tests/*.vitest.test.ts`**. - **SFC baseline**: one **`_tests/.vitest.test.ts`** per feature **`.vue`** under **`src/components/**`**, **`src/layouts/**`**, **`src/pages/**`**. Extracted **`scripts/*.ts`** → **`scripts/_tests/*.vitest.test.ts`** when real logic. Merge tests when merging modules ([code-size-decomposition.mdc](../../rules/code-size-decomposition.mdc)). - **Return object literals**: same project-wide rule — identifiers/literals only in **`return { ... }`** ([code-size-decomposition.mdc](../../rules/code-size-decomposition.mdc)). - **Floating `Window*` mounts**: stub **`FaFloatingWindowBodyTeleport`** or query **`document.body`** — see [fantasia-floating-windows](../fantasia-floating-windows/SKILL.md). - **`_data/`**: production feeds only — no Vitest suites aimed only at **`_data/`**. Fixtures inline in test files; **no** **`_tests/_data/`**. - **Style**: flat **`test`** / **`test.skip`** (no **`describe`**), JSDoc per test, titles **`Test that ...`** — see [vitest-tests.mdc](../../rules/vitest-tests.mdc). - **Typing**: no **`any`**; use **`I_`** / **`T_`** naming for imported types. ## Playwright (component + E2E) **Critical**: Playwright targets **built, production** Electron app. After **any** source change affecting exercised code: `quasar build -m electron` or **`yarn quasar:build:electron`** before Playwright. **Node.js 22.22.0+** locally. **Stale packaged bundle** — Harness starts **`Fantasia Archive.exe`** from **`dist/electron/Packaged`**, not live Vite dev server. IPC still matching pre-change behavior → rebuild before next Playwright run. **Electron `userData` isolation**: **`TEST_ENV`** **`components`** / **`e2e`** → **`%APPDATA%//playwright-user-data`** (here: **`fantasia-archive/playwright-user-data`**, not **`fantasia-archive-dev`**). [`appIdentity_manager.ts`](../../../src-electron/mainScripts/appIdentity/appIdentity_manager.ts), [`playwrightIsolatedUserDataDirName.ts`](../../../src-electron/mainScripts/appIdentity/playwrightIsolatedUserDataDirName.ts), [`playwrightUserDataReset.ts`](../../../helpers/playwrightHelpers_universal/playwrightUserDataReset.ts). Specs use **`test.describe.serial`**; **`test.beforeAll`** **`await`**s **`launchFaPlaywrightComponentHarnessWindow`** / **`launchFaPlaywrightE2eAppWindow`** (owns **`resetFaPlaywrightIsolatedUserData()`** ordering — **not** in **`test.beforeEach`**). Helpers: **`helpers/playwrightHelpers_universal`** / **`_e2e`** / **`_component`**. No **`test.describe.parallel`** unless user asks. ### Playwright `keyboard.press` and app keybinds (cross-OS) - **Module**: [`faPlaywrightKeyboardChords.ts`](../../../helpers/playwrightHelpers_universal/faPlaywrightKeyboardChords.ts) - **Defaults with `primary`**: use getters (**`getFaPlaywrightDefaultToggleDevtoolsPressString()`**, etc.) — not hardcoded **Control+F12** / **Meta+F12**. - **Overrides storing `ctrl`**: physical **Control** on every OS — **`FA_PLAYWRIGHT_PRESS_CONTROL_SHIFT_F12`**, etc. - **Monaco select all**: **`getFaPlaywrightMonacoSelectAllPressString()`** - Full policy: [playwright-tests.mdc](../../rules/playwright-tests.mdc) **Keyboard strings**; product: [fantasia-keybinds](../fantasia-keybinds/SKILL.md). ### Cross-toolchain (Storybook + Electron) - **Storybook** — [`.storybook-workspace/`](../../../.storybook-workspace/); **`staticDirs`** sync with Quasar **`public/`**. VRT: **`yarn test:storybook:visual*`** chains **`yarn storybook:build`**. Verbose: **`FA_STORYBOOK_VISUAL_VERBOSE=1`**. - **Electron `file://`** — relative **`public/`** paths when **`BASE_URL`** is **`'/'`** or empty (see **`SocialContactSingleButton.vue`**). - **Playwright** — same rebuild rule; flaky UI after green Storybook often = stale build or **`file://`** mismatch. - **Storybook VRT** — **`EXCLUDED_STORY_IDS`** in **`.storybook-workspace/visual-tests/storybook.visual.playwright.test.ts`**; empty iframe root = failure unless **`tags: ['skip-visual-render-check']`** or excluded id. **`#storybook-root`** and **`#root`** checked separately. - **VRT `maxDiffPixels`** — whole-image differing-pixel cap, not width. CI vs local: see **README** **Storybook visual baseline policy**, [storybook-stories.mdc](../../rules/storybook-stories.mdc). - **Full suite** — **`yarn testbatch:ensure:nochange`** / **`yarn testbatch:ensure:change`** — see [testing-terminal-isolation.mdc](../../rules/testing-terminal-isolation.mdc). ### Config highlights (`playwright.config.ts`) - **`outputDir`**: `test-results/playwright-artifacts`. **`testMatch`**: `src/components/**`, `e2e-tests/**`. HTML: **`test-results/playwright-report`**. Yarn scripts trim artifacts via **`.utility-scripts/playwrightWithArtifactTrim.mjs`**. - `workers: 1`, `fullyParallel: false` - Serial suites: **`launchFaPlaywright*`** + **`tearDownFaPlaywrightElectronSerialSuite`**. Video: **`FA_PLAYWRIGHT_NO_VIDEO`**; cursor: **`FA_PLAYWRIGHT_CURSOR_MARKER=0`**. ### Videos and HTML report - WebM attached per serial suite. Report **regenerates** each run — ephemeral. Agents: prefer user opens **`test-results/playwright-report/index.html`** over analyzing raw **`.webm`**. ### Component tests - **Renderer readiness**: **`waitForFaRendererContentBridgeApis`** — not bare **`page.evaluate`** for bridge globals. E2E on **`#/`**: **`waitForFaE2eRendererDomReady`**. - **Structure/locators/layout**: [playwright-tests.mdc](../../rules/playwright-tests.mdc) - **Command**: `yarn test:components` — own terminal (**`src/components`**, **`src/layouts`**, **`src/pages`**) - **Location**: `src/components|layouts|pages/**/_tests/*.playwright.test.ts` - **Single**: `yarn test:components:single --component=/` - **Picker**: `yarn test:components:list` - **Harness seed**: **`I_faComponentTestingStoreSeed`** / **`projectContentOverrides`** — see AGENTS **Component-testing seed** ### E2E tests - **Structure**: same serial pattern; **`TEST_ENV: 'e2e'`** - **Project management**: [`checkProjectManagementFlow.playwright.spec.ts`](../../../e2e-tests/checkProjectManagementFlow.playwright.spec.ts); path staging via **`playwrightE2eProjectPaths.ts`** - **Workspace sidebar**: [`checkWorkspaceSidebar.playwright.spec.ts`](../../../e2e-tests/checkWorkspaceSidebar.playwright.spec.ts); drag + **`sidebar_width`** cold restart via **`faPlaywrightE2eWorkspaceSidebar.ts`** - **Workspace docs/tree**: **`e2e-tests/checkWorkspace*.playwright.spec.ts`**; helpers **`e2eWorkspaceHierarchyTree*`** - **Command**: `yarn test:e2e` — own terminal - **Single**: `yarn test:e2e:single --spec=` (no suffix) or **`yarn test:e2e:single:ci --spec=`** - **Picker**: `yarn test:e2e:list` ### Full project gate - **`yarn testbatch:ensure:nochange`** / **`yarn testbatch:ensure:change`** — see [testing-terminal-isolation.mdc](../../rules/testing-terminal-isolation.mdc) ## Checklist when changing UI or Electron shell **During edits** ([fantasia-dev-scoped-verify](../fantasia-dev-scoped-verify/SKILL.md)): 1. Dev scoped gate — touched eslint, **`yarn lint:typescript`**, connected **`yarn vitest run`** 2. **20s** dev compile smoke — [dev-electron-compile-check.mdc](../../rules/dev-electron-compile-check.mdc) **Ship / commit / final cleanup:** 1. **`yarn testbatch:verify`** — one terminal 2. Connected Playwright after **`yarn quasar:build:electron`** — own terminals 3. Storybook in scope: **`yarn test:storybook:smoke`** + **`yarn test:storybook:visual`** (or **`testbatch:ensure:*`**) ## Choosing Vitest vs Playwright - **Vitest**: pure/data/state in `src/` — deterministic - **Playwright**: user-facing interaction, full render, built Electron runtime ## Storybook smoke checks - **`yarn storybook:run`**, **`yarn test:storybook:smoke`** - VRT: **`yarn test:storybook:visual`** / **`:update`** - Stories: `src/components/**/_tests/.stories.ts`, **`meta.title`** **`Components//`** - Layout/page stories: canvas-only, no Docs — [storybook-stories.mdc](../../rules/storybook-stories.mdc) - Storybook mocks: focused **`L_*`** imports + **`externalFileLoader.ts`** placeholders — mirror new keys from **`i18n/en-US/index.ts`** ## Types Shared types → **`types/`** (**`app/types/...`**). See [types-folder.mdc](../../rules/types-folder.mdc).