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>
This commit is contained in:
@@ -3,36 +3,82 @@ import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
// Dedicated Vitest config — intentionally trimmed down from vite.config.ts.
|
||||
// Skip Vuetify / MetaLayouts / VueRouter plugins so unit tests run fast in
|
||||
// happy-dom without loading the full Vuexy bundle. Mirrors apps/portal/vitest.config.ts.
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
imports: ['vue', '@vueuse/core'],
|
||||
dirs: ['./src/@core/utils', './src/@core/composable/', './src/composables/', './src/utils/'],
|
||||
vueTemplate: true,
|
||||
// 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)),
|
||||
}
|
||||
|
||||
// Don't write to auto-imports.d.ts — vite.config.ts owns that file
|
||||
// with the full app's auto-import set. Trimmed test-only set must
|
||||
// not clobber the IDE typings for the running dev server.
|
||||
dts: false,
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': 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: {
|
||||
environment: 'happy-dom',
|
||||
globals: true,
|
||||
include: ['tests/**/*.{test,spec}.ts', 'src/**/__tests__/**/*.{test,spec}.ts'],
|
||||
setupFiles: ['./tests/setup.ts'],
|
||||
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'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user