Testing
How to write and run tests with Vitest and Vue Test Utils.
Testing Approach
Haze Dashboard uses Vitest for unit and component testing, combined with Vue Test Utils for mounting and interacting with Vue components. Vitest is Vite-native, so it shares the same configuration and plugin pipeline as your dev server — tests run fast and resolve path aliases automatically.
Tests live alongside the code they test or in a dedicated tests/ directory. Use the .test.ts or .spec.ts file extension.
Setup
Install the testing dependencies:
npm install -D vitest @vue/test-utils @vitejs/plugin-vue happy-dom
Create a vitest.config.ts at the project root:
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
test: {
environment: 'happy-dom',
globals: true,
include: ['tests/**/*.test.ts', 'app/**/*.test.ts'],
},
resolve: {
alias: {
'~': resolve(__dirname, 'app'),
'#imports': resolve(__dirname, '.nuxt/imports.d.ts'),
},
},
})Writing a Component Test
Test that a component renders correctly with given props and responds to user interaction:
// tests/components/StatusBadge.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import StatusBadge from '~/components/shared/StatusBadge.vue'
describe('SharedStatusBadge', () => {
it('renders the status text', () => {
const wrapper = mount(StatusBadge, {
props: { status: 'active' },
})
expect(wrapper.text()).toContain('active')
})
it('applies success color for completed status', () => {
const wrapper = mount(StatusBadge, {
props: { status: 'completed' },
})
expect(wrapper.html()).toContain('success')
})
it('applies error color for cancelled status', () => {
const wrapper = mount(StatusBadge, {
props: { status: 'cancelled' },
})
expect(wrapper.html()).toContain('error')
})
})Writing a Page Test
Page tests verify that a page component renders its main elements. Since pages may use auto-imported composables and components, you may need to provide stubs:
// tests/pages/Dashboard.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import DashboardPage from '~/pages/dashboard/index.vue'
describe('Dashboard Page', () => {
it('renders the page heading', () => {
const wrapper = mount(DashboardPage, {
global: {
stubs: {
SharedPageHeader: {
template: ' ',
props: ['title'],
},
SharedGlassCard: {
template: ' ',
},
ClientOnly: {
template: ' ',
},
},
},
})
expect(wrapper.html()).toBeTruthy()
})
})Testing Composables
Test composables by calling them within a Vue component context. Use a small wrapper component or withSetup helper:
// tests/composables/useThemeSettings.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
describe('useThemeSettings', () => {
beforeEach(() => {
localStorage.clear()
})
it('returns default settings', () => {
// Note: testing composables that use useLocalStorage
// requires the happy-dom environment
const settings = {
accentColor: 'teal',
density: 'default',
rtl: false,
}
expect(settings.accentColor).toBe('teal')
expect(settings.density).toBe('default')
expect(settings.rtl).toBe(false)
})
it('persists accent color changes', () => {
localStorage.setItem('haze-theme', JSON.stringify({
accentColor: 'blue',
density: 'default',
rtl: false,
}))
const stored = JSON.parse(localStorage.getItem('haze-theme')!)
expect(stored.accentColor).toBe('blue')
})
})Running Tests
| Command | Description |
|---|---|
npx vitest | Run tests in watch mode (re-runs on file changes) |
npx vitest run | Run all tests once and exit (CI-friendly) |
npx vitest --ui | Open the interactive Vitest UI in a browser |
npx vitest --coverage | Generate code coverage report (requires @vitest/coverage-v8) |
npx vitest run tests/components/ | Run tests in a specific directory |
Mocking API Routes
Components that use useFetch can be tested by mocking the composable:
// tests/components/OrdersList.test.ts
import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
// Mock useFetch globally
vi.mock('#imports', () => ({
useFetch: vi.fn(() => ({
data: ref({
data: [
{ id: 1, orderNumber: 'ORD-001', status: 'active' },
{ id: 2, orderNumber: 'ORD-002', status: 'pending' },
],
meta: { total: 2, page: 1, perPage: 10, lastPage: 1 },
}),
pending: ref(false),
error: ref(null),
})),
ref: (v: any) => ({ value: v }),
}))Tip
Mock server routes with vi.mock() for API-dependent components. For simpler components that only take props, you can test them directly without mocking.
Next Steps
Ready to go live? See the Deployment guide for step-by-step instructions on deploying to Vercel, Netlify, Cloudflare Pages, or a Node.js server.