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:
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 configuration. Currently configured with Storybook test project
Enables browser-based testing with Playwright
Runs browser tests in headless mode (no UI)
Browser instances to use. Currently set to Chromium
Path aliases for imports: @/, @app/, @shared/, @features/
Package Configuration
Test scripts are defined in 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:
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:
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:
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.
Recommended Scripts
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();
});
});
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:
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
Test behavior, not implementation
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
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.