Testing

Vitest setup, test patterns, and mocking strategy

Testing

What's Included

  • Vitest with jsdom environment
  • Vue Test Utils for component testing
  • Global mocks for Nuxt composables, components, and stores
  • Unit tests for components and composables
  • Integration tests for full page flows

Setup

Vitest Config (vitest.config.ts)

export default defineConfig({
  test: {
    environment: 'jsdom',
    globals: true,
    setupFiles: ['tests/setup.ts'],
    alias: {
      '@/': fileURLToPath(new URL('./', import.meta.url)),
      '~/': fileURLToPath(new URL('./', import.meta.url))
    }
  }
})

Test Setup (tests/setup.ts)

The setup file provides global mocks for:

  • Nuxt componentsNuxtLink, NuxtPage (rendered as simple HTML elements)
  • ComposablesuseAuth, useProfile, useSubscription, useCheckout, usePlans, useScrollspy
  • StoresuseAuthStore (with mock token, user, isAuthenticated)
  • Vue composablesref, computed, watch, onMounted, etc.
  • Runtime configuseRuntimeConfig with test API URL
  • Fetch$fetch and useFetch mocked globally
  • Firebase$firebaseAuth, $firebaseApp mocked

Running Tests

# All tests
docker compose exec app npm run test

# Watch mode
docker compose exec app npm run test:watch

# Coverage
docker compose exec app npm run test:coverage

Test Structure

tests/
├── setup.ts                           # Global mocks
├── unit/
│   ├── components/                    # Component unit tests
│   └── composables/                   # Composable unit tests
└── integration/                       # Full page integration tests
    ├── landing/
    ├── auth/
    ├── payments/
    └── navigation/

Writing Component Tests

import { mount } from '@vue/test-utils'
import LoginForm from '~/components/auth/LoginForm.vue'

describe('LoginForm', () => {
  it('renders login form fields', () => {
    const wrapper = mount(LoginForm)

    expect(wrapper.find('input[type="email"]').exists()).toBe(true)
    expect(wrapper.find('input[type="password"]').exists()).toBe(true)
    expect(wrapper.find('button[type="submit"]').exists()).toBe(true)
  })

  it('calls login on form submit', async () => {
    const wrapper = mount(LoginForm)
    // ... set input values and submit
  })
})

Mocking Strategy

The project uses global mocks defined in tests/setup.ts rather than per-test mocking. This ensures consistency and avoids boilerplate. Composables return mock functions that can be spied on in individual tests.