test(apps/app): set up Vitest harness — closes TECH-APP-VITEST (WS-6)

Mirrors apps/portal's Vitest setup so the SPA can take frontend
unit + component tests. Required prerequisite for WS-6 sessie 3b's
admin UI work — apps/portal had 113+ tests, apps/app had zero, and
launching WS-6's organizer UI uncovered while the portal SPA is
well-tested would be asymmetric quality.

Setup:
- vitest, happy-dom, @vue/test-utils, @testing-library/vue installed
- vitest.config.ts mirrors portal config: trimmed auto-imports
  (no pinia/vue-router/vue-i18n/@vueuse/math) so tests run fast
  in happy-dom without loading the full Vuexy bundle
- AutoImport's dts:false prevents the trimmed test-only set from
  clobbering the dev-server's full auto-imports.d.ts (apps/app's
  auto-import surface is bigger than the portal's)
- tests/setup.ts mocks vue-router by default; tests that exercise
  the real router can override per-suite
- Sample sanity test confirms the harness works end-to-end

Adds `pnpm test` and `pnpm test:watch` scripts to package.json.

Refs: BACKLOG TECH-APP-VITEST, WS-6 sessie 3b prerequisite

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 21:34:27 +02:00
parent 8a4682ab35
commit d95e68423d
5 changed files with 718 additions and 2 deletions

View File

@@ -11,7 +11,9 @@
"lint": "eslint . -c .eslintrc.cjs --fix --ext .ts,.js,.cjs,.vue,.tsx,.jsx",
"build:icons": "tsx src/plugins/iconify/build-icons.ts",
"msw:init": "msw init public/ --save",
"postinstall": "npm run build:icons && npm run msw:init"
"postinstall": "npm run build:icons && npm run msw:init",
"test": "vitest run",
"test:watch": "vitest"
},
"dependencies": {
"@casl/ability": "6.7.3",
@@ -78,6 +80,7 @@
"@intlify/unplugin-vue-i18n": "11.0.1",
"@stylistic/stylelint-config": "1.0.1",
"@stylistic/stylelint-plugin": "2.1.3",
"@testing-library/vue": "^8.1.0",
"@tiptap/extension-character-count": "^2.27.1",
"@tiptap/extension-placeholder": "^2.27.1",
"@tiptap/extension-subscript": "^2.27.1",
@@ -92,6 +95,7 @@
"@typescript-eslint/parser": "7.18.0",
"@vitejs/plugin-vue": "6.0.1",
"@vitejs/plugin-vue-jsx": "5.1.1",
"@vue/test-utils": "^2.4.9",
"baseline-browser-mapping": "^2.10.16",
"eslint": "8.57.1",
"eslint-config-airbnb-base": "15.0.0",
@@ -103,6 +107,7 @@
"eslint-plugin-sonarjs": "0.24.0",
"eslint-plugin-unicorn": "51.0.1",
"eslint-plugin-vue": "9.33.0",
"happy-dom": "^20.9.0",
"msw": "2.6.8",
"postcss-html": "1.8.0",
"postcss-scss": "4.0.9",
@@ -123,6 +128,7 @@
"vite-plugin-vue-meta-layouts": "0.6.1",
"vite-plugin-vuetify": "2.1.2",
"vite-svg-loader": "5.1.0",
"vitest": "^4.1.5",
"vue-shepherd": "3.0.0",
"vue-tsc": "3.1.2"
},
@@ -137,4 +143,4 @@
"msw": {
"workerDirectory": "public"
}
}
}