Skip to main content
The Auction Platform uses Vitest for unit and component testing, with Playwright for browser-based testing.

Testing Stack

Vitest

Fast unit test runner powered by Vite

Playwright

Browser automation for component and E2E tests

Storybook Tests

Component testing through Storybook stories

Coverage

Code coverage with @vitest/coverage-v8

Configuration

Testing is configured in vite.config.ts:
vite.config.ts
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
import { tanstackRouter } from '@tanstack/router-plugin/vite';
import react from '@vitejs/plugin-react-swc';
import { playwright } from '@vitest/browser-playwright';
import path from 'node:path';
import { defineConfig } from 'vite';

export default defineConfig(({ mode }) => ({
  plugins: [
    tanstackRouter({
      target: 'react',
      autoCodeSplitting: true,
    }),
    react(),
  ],

  test: {
    projects: [
      {
        extends: true,
        plugins: [
          storybookTest({
            configDir: path.join(dirname, '.storybook'),
          }),
        ],
        test: {
          name: 'storybook',
          browser: {
            enabled: true,
            headless: true,
            provider: playwright({}),
            instances: [{ browser: 'chromium' }],
          },
          setupFiles: ['.storybook/vitest.setup.ts'],
        },
      },
    ],
  },

  resolve: {
    alias: {
      '@': path.resolve(dirname, './src'),
      "@app": path.resolve(__dirname, "src/app"),
      "@shared": path.resolve(__dirname, "src/shared"),
      "@features": path.resolve(__dirname, "src/features"),
    },
  },
}));

Configuration Details

test.projects
array
Test projects configuration. Currently configured with Storybook test project
test.browser.enabled
boolean
Enables browser-based testing with Playwright
test.browser.headless
boolean
Runs browser tests in headless mode (no UI)
test.browser.instances
array
Browser instances to use. Currently set to Chromium
resolve.alias
object
Path aliases for imports: @/, @app/, @shared/, @features/

Package Configuration

Test scripts are defined in package.json:
package.json
{
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "devDependencies": {
    "@storybook/addon-vitest": "^10.2.12",
    "@vitest/browser-playwright": "^4.0.18",
    "@vitest/coverage-v8": "^4.0.18",
    "playwright": "^1.58.2",
    "vitest": "^4.0.18"
  }
}
The project uses Vitest 4.x with native browser testing support through Playwright.

Testing Strategy

The project follows a multi-layered testing strategy:

1. Component Testing via Storybook

Components are tested through their Storybook stories:
Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { expect, userEvent, within } from '@storybook/test';
import Button from './Button';

const meta: Meta<typeof Button> = {
  title: 'Shared/Button',
  component: Button,
};

export default meta;
type Story = StoryObj<typeof Button>;

export const Primary: Story = {
  args: {
    children: 'Click Me',
    variant: 'primary',
  },
};

// Test interactions
export const ClickTest: Story = {
  args: {
    children: 'Click Me',
    onClick: () => console.log('clicked'),
  },
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    const button = canvas.getByRole('button');
    
    // Verify button is rendered
    await expect(button).toBeInTheDocument();
    
    // Simulate click
    await userEvent.click(button);
  },
};

2. Browser Testing with Playwright

Vitest uses Playwright for browser-based tests:
component.test.ts
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from '@app/providers/Theme';
import Button from './Button';

describe('Button', () => {
  it('renders with correct text', async () => {
    render(
      <ThemeProvider>
        <Button>Click Me</Button>
      </ThemeProvider>
    );
    
    const button = screen.getByRole('button', { name: 'Click Me' });
    expect(button).toBeInTheDocument();
  });
  
  it('applies variant classes', async () => {
    render(
      <ThemeProvider>
        <Button variant="primary">Primary</Button>
      </ThemeProvider>
    );
    
    const button = screen.getByRole('button');
    expect(button).toHaveClass('primary');
  });
});

3. Unit Testing

Test utility functions and business logic:
validation.test.ts
import { describe, it, expect } from 'vitest';
import { attachValidators } from '@shared/validation-engine/attachValidators';

describe('attachValidators', () => {
  it('attaches required validator', () => {
    const field = {
      id: 'email',
      fieldValidators: [{ type: 'required' as const }]
    };
    
    const validated = attachValidators(field);
    const error = validated.fieldValidators[0]('', 'email');
    
    expect(error).toBe('Field is required');
  });
  
  it('attaches minLength validator', () => {
    const field = {
      id: 'username',
      fieldValidators: [
        { type: 'minLength' as const, constraints: { minLength: 3 } }
      ]
    };
    
    const validated = attachValidators(field);
    const error = validated.fieldValidators[0]('ab', 'username');
    
    expect(error).toBe('Must be at least 3 characters');
  });
});

Running Tests

The project currently doesn’t have explicit test scripts in package.json. You may need to add them.
Add these scripts to package.json:
{
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:run": "vitest run",
    "test:coverage": "vitest run --coverage"
  }
}
npm run test
# Runs tests in watch mode

Test File Organization

Organize test files next to the code they test:
src/
├── shared/
│   ├── ui/
│   │   ├── button/
│   │   │   ├── Button.tsx
│   │   │   ├── Button.test.tsx       # Component tests
│   │   │   ├── Button.stories.tsx    # Storybook tests
│   │   │   └── button.module.css
│   │   └── TextField/
│   │       ├── TextField.tsx
│   │       ├── TextField.test.tsx
│   │       └── TextField.stories.tsx
│   └── validation-engine/
│       ├── attachValidators.ts
│       ├── attachValidators.test.ts  # Unit tests
│       └── validatiorRegistery.ts
└── features/
    └── auth/
        ├── LoginForm.tsx
        └── LoginForm.test.tsx

Writing Tests

Component Test Template

import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ThemeProvider } from '@app/providers/Theme';
import YourComponent from './YourComponent';

describe('YourComponent', () => {
  it('renders correctly', () => {
    render(
      <ThemeProvider>
        <YourComponent />
      </ThemeProvider>
    );
    
    expect(screen.getByText('Expected Text')).toBeInTheDocument();
  });
});

Unit Test Template

import { describe, it, expect } from 'vitest';
import { yourFunction } from './yourModule';

describe('yourFunction', () => {
  it('returns expected value', () => {
    const result = yourFunction('input');
    expect(result).toBe('expected');
  });
  
  it('handles edge cases', () => {
    expect(yourFunction('')).toBe(null);
    expect(yourFunction(null)).toBe(null);
  });
});

Testing Patterns

Testing Theme-Aware Components

import { describe, it, expect } from 'vitest';
import { render } from '@testing-library/react';
import { ThemeProvider } from '@app/providers/Theme';
import ThemedComponent from './ThemedComponent';

describe('ThemedComponent', () => {
  it('applies theme styles', () => {
    const { container } = render(
      <ThemeProvider>
        <ThemedComponent />
      </ThemeProvider>
    );
    
    // Check that theme data attribute is set
    expect(document.documentElement.dataset.theme).toBeDefined();
  });
});

Testing Form Validation

import { describe, it, expect } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from './LoginForm';

describe('LoginForm', () => {
  it('shows validation errors', async () => {
    const user = userEvent.setup();
    render(<LoginForm />);
    
    const submitButton = screen.getByRole('button', { name: 'Submit' });
    await user.click(submitButton);
    
    await waitFor(() => {
      expect(screen.getByText('Field is required')).toBeInTheDocument();
    });
  });
});

Testing Async Operations

import { describe, it, expect, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import DataComponent from './DataComponent';

describe('DataComponent', () => {
  it('loads data', async () => {
    // Mock fetch
    global.fetch = vi.fn(() =>
      Promise.resolve({
        json: () => Promise.resolve({ data: 'test' }),
      })
    );
    
    render(<DataComponent />);
    
    await waitFor(() => {
      expect(screen.getByText('test')).toBeInTheDocument();
    });
  });
});

Code Coverage

Generate coverage reports with the @vitest/coverage-v8 package:
vitest run --coverage
Coverage reports are generated in the coverage/ directory.

Coverage Configuration

Add coverage configuration to vite.config.ts:
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'html', 'json'],
      include: ['src/**/*.{ts,tsx}'],
      exclude: [
        'src/**/*.stories.tsx',
        'src/**/*.test.tsx',
        'src/tests/**',
      ],
    },
  },
});

Best Practices

Focus on what the component does, not how it does it:
// Good - tests behavior
expect(screen.getByRole('button')).toBeDisabled();

// Avoid - tests implementation
expect(component.state.disabled).toBe(true);
Prefer accessible queries:
// Good
screen.getByRole('button', { name: 'Submit' })
screen.getByLabelText('Email')

// Avoid
screen.getByTestId('submit-button')
Use Storybook’s a11y addon to catch accessibility issues early.
Each test should be independent and not rely on other tests.

Debugging Tests

Debug in Browser

vitest --ui
Opens Vitest UI where you can debug tests in the browser.

Debug with VS Code

Add this to .vscode/launch.json:
{
  "type": "node",
  "request": "launch",
  "name": "Debug Vitest Tests",
  "runtimeExecutable": "npm",
  "runtimeArgs": ["run", "test"],
  "console": "integratedTerminal",
  "internalConsoleOptions": "neverOpen"
}

Resources

Vitest Docs

Official Vitest documentation

Playwright Docs

Playwright testing library docs

Testing Library

React Testing Library guides

Storybook Testing

Testing with Storybook stories
The project uses Vitest’s browser mode with Playwright for accurate component testing in real browsers.

Build docs developers (and LLMs) love