2025-11-22 16:42:35 +01:00
|
|
|
import { config } from '@vue/test-utils'
|
|
|
|
|
import { vi } from 'vitest'
|
|
|
|
|
import * as Vue from 'vue'
|
|
|
|
|
|
2025-11-22 19:49:53 +01:00
|
|
|
// Mock localStorage with actual implementation
|
|
|
|
|
class LocalStorageMock {
|
|
|
|
|
private store: Map<string, string> = new Map()
|
|
|
|
|
|
|
|
|
|
getItem(key: string): string | null {
|
|
|
|
|
return this.store.get(key) ?? null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setItem(key: string, value: string): void {
|
|
|
|
|
this.store.set(key, value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeItem(key: string): void {
|
|
|
|
|
this.store.delete(key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clear(): void {
|
|
|
|
|
this.store.clear()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key(index: number): string | null {
|
|
|
|
|
return Array.from(this.store.keys())[index] ?? null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get length(): number {
|
|
|
|
|
return this.store.size
|
|
|
|
|
}
|
2025-11-22 16:42:35 +01:00
|
|
|
}
|
2025-11-22 19:49:53 +01:00
|
|
|
|
|
|
|
|
global.localStorage = new LocalStorageMock() as unknown as Storage
|
2025-11-22 16:42:35 +01:00
|
|
|
|
|
|
|
|
// Make Vue composables globally available
|
|
|
|
|
global.ref = Vue.ref
|
|
|
|
|
global.computed = Vue.computed
|
|
|
|
|
global.reactive = Vue.reactive
|
|
|
|
|
global.watch = Vue.watch
|
|
|
|
|
global.watchEffect = Vue.watchEffect
|
|
|
|
|
global.defineModel = Vue.defineModel
|
|
|
|
|
global.onMounted = Vue.onMounted
|
|
|
|
|
global.onUnmounted = Vue.onUnmounted
|
|
|
|
|
global.nextTick = Vue.nextTick
|
|
|
|
|
|
|
|
|
|
// Mock Nuxt composables
|
|
|
|
|
global.useRuntimeConfig = vi.fn(() => ({
|
|
|
|
|
public: {},
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
global.useNuxtApp = vi.fn(() => ({
|
|
|
|
|
$apollo: {},
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
global.navigateTo = vi.fn()
|
|
|
|
|
global.definePageMeta = vi.fn()
|
|
|
|
|
|
|
|
|
|
// Mock Vuetify components globally for tests
|
|
|
|
|
config.global.stubs = {
|
|
|
|
|
VAlert: {
|
|
|
|
|
name: 'v-alert',
|
|
|
|
|
template: '<div class="v-alert" :type="type" :variant="variant" :color="color"><slot /></div>',
|
|
|
|
|
props: ['color', 'type', 'variant'],
|
|
|
|
|
},
|
|
|
|
|
VTextField: {
|
|
|
|
|
template: '<div class="v-text-field"><input :readonly="readonly || disabled" :disabled="disabled" :error="error" :value="modelValue" @input="$emit(\'update:model-value\', $event.target.value)" @blur="$emit(\'blur\')" /></div>',
|
|
|
|
|
props: ['modelValue', 'readonly', 'disabled', 'active', 'focused', 'variant', 'error', 'errorMessages', 'label'],
|
|
|
|
|
emits: ['update:model-value', 'blur'],
|
|
|
|
|
},
|
|
|
|
|
VBtn: {
|
|
|
|
|
name: 'v-btn',
|
|
|
|
|
template: '<button :disabled="disabled" :color="color" :data-testid="$attrs[\'data-testid\']" @click="$emit(\'click\')"><slot /></button>',
|
|
|
|
|
props: ['disabled', 'variant', 'icon', 'color', 'flat', 'depressed', 'xLarge'],
|
|
|
|
|
emits: ['click'],
|
|
|
|
|
},
|
|
|
|
|
VDialog: {
|
|
|
|
|
name: 'v-dialog',
|
|
|
|
|
template: '<div class="v-dialog"><slot name="activator" /><div class="v-dialog__content"><slot /></div></div>',
|
|
|
|
|
props: ['modelValue', 'width', 'maxWidth', 'persistent'],
|
|
|
|
|
},
|
|
|
|
|
VForm: {
|
|
|
|
|
name: 'v-form',
|
|
|
|
|
template: '<form @submit="$emit(\'submit\', $event)"><slot /></form>',
|
|
|
|
|
emits: ['submit'],
|
|
|
|
|
},
|
|
|
|
|
VCard: {
|
|
|
|
|
name: 'v-card',
|
|
|
|
|
template: '<div class="v-card" :variant="variant" :rounded="rounded" :loading="loading"><div v-if="title" class="v-card-title">{{ title }}</div><slot /></div>',
|
|
|
|
|
props: ['variant', 'rounded', 'loading', 'title'],
|
|
|
|
|
},
|
|
|
|
|
VCardTitle: {
|
|
|
|
|
template: '<div class="v-card-title"><slot /></div>',
|
|
|
|
|
},
|
|
|
|
|
VCardText: {
|
|
|
|
|
name: 'v-card-text',
|
|
|
|
|
template: '<div class="v-card-text"><slot /></div>',
|
|
|
|
|
},
|
|
|
|
|
VCardActions: {
|
|
|
|
|
name: 'v-card-actions',
|
|
|
|
|
template: '<div class="v-card-actions"><slot /></div>',
|
|
|
|
|
},
|
|
|
|
|
VSpacer: {
|
|
|
|
|
name: 'v-spacer',
|
|
|
|
|
template: '<div class="v-spacer"></div>',
|
|
|
|
|
},
|
|
|
|
|
VIcon: {
|
|
|
|
|
name: 'v-icon',
|
|
|
|
|
template: '<i class="v-icon" :class="icon" @click="$emit(\'click\')"></i>',
|
2025-11-22 19:49:53 +01:00
|
|
|
props: ['icon', 'large', 'start', 'end', 'size'],
|
2025-11-22 16:42:35 +01:00
|
|
|
emits: ['click'],
|
|
|
|
|
},
|
2025-11-22 19:49:53 +01:00
|
|
|
VMenu: {
|
|
|
|
|
name: 'v-menu',
|
|
|
|
|
template: '<div class="v-menu"><slot name="activator" :props="{}" /><slot /></div>',
|
|
|
|
|
props: ['modelValue', 'closeOnContentClick'],
|
|
|
|
|
},
|
|
|
|
|
VList: {
|
|
|
|
|
name: 'v-list',
|
|
|
|
|
template: '<div class="v-list"><slot /></div>',
|
|
|
|
|
},
|
|
|
|
|
VListItem: {
|
|
|
|
|
name: 'v-list-item',
|
|
|
|
|
template: '<div class="v-list-item" :data-active="active" @click="$emit(\'click\')"><slot /><slot name="append" /></div>',
|
|
|
|
|
props: ['active', 'value'],
|
|
|
|
|
emits: ['click'],
|
|
|
|
|
},
|
|
|
|
|
VListItemTitle: {
|
|
|
|
|
name: 'v-list-item-title',
|
|
|
|
|
template: '<div class="v-list-item-title"><slot /></div>',
|
|
|
|
|
},
|
|
|
|
|
VChip: {
|
|
|
|
|
name: 'v-chip',
|
|
|
|
|
template: '<div class="v-chip" :class="variant"><slot /></div>',
|
|
|
|
|
props: ['size', 'variant', 'prependIcon'],
|
|
|
|
|
},
|
2025-11-22 16:42:35 +01:00
|
|
|
}
|