Skip to content

Frontend Testing with Vitest

This document explains how to use the Vitest testing setup for the tracker-frontend and tracker-admin projects.

Overview

Both frontend projects are configured with:

  • Vitest for unit testing
  • @testing-library/react for React component testing
  • Coverage reporting with v8 provider
  • SonarQube integration via LCOV reports

Quick Start

Running Tests

Individual Projects

# Frontend project
cd tracker-frontend
npm run test                # Run tests in watch mode
npm run test:run           # Run tests once
npm run test:coverage      # Run tests with coverage
npm run test:ui            # Run tests with UI interface

# Admin project
cd tracker-admin
npm run test               # Run tests in watch mode
npm run test:run          # Run tests once
npm run test:coverage     # Run tests with coverage
npm run test:ui           # Run tests with UI interface

Both Projects at Once

# From root directory
./run_frontend_tests.sh

In Development Containers

# Frontend container
docker compose exec frontend-dev npm run test:coverage

# Admin container
docker compose exec admin-dev npm run test:coverage

Configuration

Vitest Configuration

  • Config files: vitest.config.ts in each project
  • Test environment: jsdom (for DOM APIs)
  • Coverage provider: v8
  • Coverage formats: text, json, html, lcov

Test Setup

  • Setup files: src/test/setup.ts in each project
  • Global mocks: DOM APIs, localStorage, Leaflet, react-leaflet
  • Testing utilities: @testing-library/jest-dom matchers

Coverage Settings

  • Thresholds: 80% for branches, functions, lines, statements
  • Excluded files:
  • node_modules/
  • test files
  • config files
  • build/dist directories
  • main.tsx and vite-env.d.ts

Writing Tests

Basic Test Structure

import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import { MyComponent } from './MyComponent'

describe('MyComponent', () => {
  it('should render correctly', () => {
    render(<MyComponent />)
    expect(screen.getByText('Hello World')).toBeInTheDocument()
  })
})

Testing React Components

import { describe, it, expect, vi } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

describe('Button Component', () => {
  it('should call onClick when clicked', async () => {
    const user = userEvent.setup()
    const handleClick = vi.fn()

    render(<Button onClick={handleClick}>Click me</Button>)

    await user.click(screen.getByRole('button'))
    expect(handleClick).toHaveBeenCalledOnce()
  })
})

Testing Utilities/Hooks

import { describe, it, expect } from "vitest";
import { renderHook } from "@testing-library/react";
import { useCustomHook } from "./useCustomHook";

describe("useCustomHook", () => {
  it("should return expected value", () => {
    const { result } = renderHook(() => useCustomHook());
    expect(result.current.value).toBe("expected");
  });
});

Available Mocks

Global Mocks (automatically available)

  • window.matchMedia
  • ResizeObserver
  • IntersectionObserver
  • localStorage / sessionStorage
  • URL.createObjectURL / URL.revokeObjectURL
  • fetch

Leaflet Mocks

  • leaflet - All Leaflet APIs are mocked
  • react-leaflet - All react-leaflet components are mocked

Custom Mocks

import { vi } from "vitest";

// Mock a module
vi.mock("./api/client", () => ({
  fetchData: vi.fn(() => Promise.resolve({ data: "mocked" })),
}));

// Mock environment variables
vi.stubEnv("VITE_API_URL", "http://test-api.com");

Coverage Reports

Generated Files

  • HTML Report: coverage/index.html (viewable in browser)
  • LCOV Report: coverage/lcov.info (for SonarQube)
  • JSON Report: coverage/coverage-final.json
  • Text Report: Displayed in terminal

Viewing Coverage

# Generate and view HTML coverage report
npm run test:coverage
open coverage/index.html  # macOS
xdg-open coverage/index.html  # Linux

SonarQube Integration

Configuration

The sonar-project.properties file is configured to pick up frontend coverage:

sonar.javascript.lcov.reportPaths=tracker-frontend/coverage/lcov.info,tracker-admin/coverage/lcov.info

Workflow

  1. Run tests with coverage: ./run_frontend_tests.sh
  2. Submit to SonarQube: sonar-scanner
  3. View results in SonarQube dashboard

VSCode Integration

Extensions

Install the Vitest extension for VSCode to get:

  • Test discovery in the sidebar
  • Inline test results
  • Debug support
  • Coverage visualization

Settings

The project includes Vitest support in .vscode/settings.json:

{
  "vitest.enable": true
}

Troubleshooting

Common Issues

Tests not found

  • Ensure test files end with .test.ts or .test.tsx
  • Check that test files are in the src/ directory
  • Verify vitest.config.ts is properly configured

Coverage not generated

  • Ensure you're using npm run test:coverage
  • Check that the coverage directory has write permissions
  • Verify v8 coverage provider is installed

Mock issues

  • Check that mocks are defined before imports
  • Use vi.hoisted() for hoisted mocks if needed
  • Ensure mock paths match actual module paths

Container testing

  • Ensure containers are running: docker compose up -d
  • Check that node_modules are properly mounted
  • Verify npm scripts work inside containers

Debug Mode

# Run tests in debug mode
npm run test -- --reporter=verbose

# Run specific test file
npm run test -- src/components/MyComponent.test.tsx

# Run tests matching pattern
npm run test -- --grep "should render"

Best Practices

Test Organization

  • Group related tests with describe blocks
  • Use descriptive test names
  • Follow AAA pattern: Arrange, Act, Assert

Mocking Strategy

  • Mock external dependencies
  • Use real implementations for internal utilities
  • Mock at the module boundary, not implementation details

Coverage Goals

  • Aim for 80%+ coverage on critical paths
  • Focus on business logic over UI components
  • Test error conditions and edge cases

Performance

  • Use vi.fn() for simple mocks
  • Avoid unnecessary DOM rendering in unit tests
  • Clean up after tests with afterEach hooks

Examples

See the example test files:

  • tracker-frontend/src/test/example.test.ts
  • tracker-admin/src/test/example.test.ts

For more examples, refer to the Vitest documentation and Testing Library documentation.