Files
crewli/apps/app/vitest.config.ts
bert.hausmans 5f135ec2b9 test: add mountWithVuexy helper, install axe-core, segment vitest configs
Foundation for the upcoming component / integration / a11y tests.

vitest.config.ts now declares two projects:
  - "unit"      — pure-logic tests under tests/unit/, src/**/__tests__/,
                  and tests/*.spec.ts (the legacy sanity test).
                  happy-dom, no Vuetify, fast path.
  - "component" — tests under tests/component/, tests/integration/,
                  tests/a11y/. jsdom, Vuetify inlined via SSR noExternal,
                  CSS imports processed (so :root token sheet loads), and
                  no global vue-router mock so the real router can run.

Both share the same alias map and AutoImport bag.

tests/utils/mountWithVuexy.ts (new):
  - Real Vuetify with the Crewli theme tokens
  - createTestingPinia (actions execute by default; stubActions opt-in)
  - vue-router with memory history at the configured initialPath + ?query
  - Fresh QueryClient per call (zero cross-test cache leak)
  - Notification mock injected via Pinia plugin so any useNotificationStore()
    resolves to { show: vi.fn(), hide: vi.fn() } — matches the actual
    NotificationStore API surface (per Phase A finding A4)
  - Imports `@/styles/tokens/_timetable.css` at module load so JSDOM resolves
    var(--tt-…) when components call getComputedStyle()

tests/setup.component.ts (new):
  - vitest-axe matcher registration
  - JSDOM polyfills: scrollIntoView, ResizeObserver, visualViewport, body
    bounding rect — Vuetify menus / overlays would crash without them
  - Deterministic crypto polyfill (mirrors tests/setup.ts so
    generateIdempotencyKey() is stable, but without the router mock)

tests/component/_smoke.test.ts (new):
  - Mounts a trivial component → asserts wrapper, queryClient, pinia,
    router, notificationMock all populated
  - Calls getComputedStyle(documentElement).getPropertyValue('--tt-status-confirmed-bg')
    → asserts '#e8f8f0' (proves the CSS token sheet really loaded)

devDependencies added: jsdom, axe-core, vitest-axe, @pinia/testing.

Total: 319 → 321 tests; 42 → 43 files. Both projects green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 03:27:31 +02:00

85 lines
3.0 KiB
TypeScript

import { fileURLToPath } from 'node:url'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import { defineConfig } from 'vitest/config'
// Two projects share one config:
//
// - "unit" — pure-logic tests under tests/unit/ + src/**/__tests__/.
// No Vuetify, no SCSS plugin, happy-dom only. Fast path.
// - "component" — component / integration / a11y tests under tests/component/,
// tests/integration/, tests/a11y/. Loads CSS imports so
// `import '@/styles/tokens/_timetable.css'` resolves and
// getComputedStyle() returns var(--tt-…) values in jsdom.
//
// Both share the same alias map and AutoImport bag so test paths and the
// auto-imported `ref/computed/watch` etc. work identically.
const sharedAliases = {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'@core': fileURLToPath(new URL('./src/@core', import.meta.url)),
'@layouts': fileURLToPath(new URL('./src/@layouts', import.meta.url)),
'@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)),
'@styles': fileURLToPath(new URL('./src/assets/styles/', import.meta.url)),
}
const sharedAutoImport = AutoImport({
imports: ['vue', '@vueuse/core'],
dirs: ['./src/@core/utils', './src/@core/composable/', './src/composables/', './src/utils/'],
vueTemplate: true,
dts: false,
})
export default defineConfig({
test: {
projects: [
{
plugins: [vue(), sharedAutoImport],
resolve: { alias: sharedAliases },
test: {
name: 'unit',
environment: 'happy-dom',
globals: true,
include: [
'tests/unit/**/*.{test,spec}.ts',
'tests/*.{test,spec}.ts',
'src/**/__tests__/**/*.{test,spec}.ts',
],
setupFiles: ['./tests/setup.ts'],
},
},
{
plugins: [vue(), sharedAutoImport],
resolve: { alias: sharedAliases },
// Inline Vuetify so its ESM bits are processed by Vite's transform.
ssr: { noExternal: ['vuetify'] },
test: {
name: 'component',
environment: 'jsdom',
globals: true,
include: [
'tests/component/**/*.{test,spec}.ts',
'tests/integration/**/*.{test,spec}.ts',
'tests/a11y/**/*.{test,spec}.ts',
],
// Intentionally NOT including ./tests/setup.ts — it stubs `vue-router`
// globally for the unit project, which would defeat the real router
// wired by mountWithVuexy. setup.component.ts handles its own
// crypto/JSDOM stubs.
setupFiles: ['./tests/setup.component.ts'],
// CSS @import statements (e.g. `@/styles/tokens/_timetable.css`)
// need to actually load so getComputedStyle resolves CSS variables.
css: true,
server: {
deps: {
inline: ['vuetify'],
},
},
},
},
],
},
})