This document describes the complete Vitest testing setup for the VTChat monorepo. Vitest has been successfully integrated to provide fast, modern testing capabilities for both React components and utility functions across all packages.
{
"devDependencies": {
"vitest": "^3.2.4",
"@vitest/ui": "^3.2.4",
"@vitest/coverage-v8": "^3.2.4",
"@testing-library/react": "^16.3.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/user-event": "^14.6.1",
"@vitejs/plugin-react": "^4.5.2",
"jsdom": "^26.1.0",
"vite-tsconfig-paths": "^5.1.4"
}
}import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [tsconfigPaths(), react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./vitest.setup.ts'],
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
exclude: [
'**/node_modules/**',
'**/dist/**',
'**/.{idea,git,cache,output,temp}/**',
'**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*',
'**/apps/web/.next/**',
],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'coverage/**',
'dist/**',
'**/node_modules/**',
'**/.next/**',
'**/*.d.ts',
'**/*.config.*',
'**/apps/web/app/tests/**',
'vitest.setup.ts',
],
},
},
});import '@testing-library/jest-dom/vitest';
import { cleanup } from '@testing-library/react';
import { afterEach, beforeAll, vi } from 'vitest';
// Mock IntersectionObserver for components that use it
beforeAll(() => {
global.IntersectionObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
// Mock ResizeObserver for components that use it
global.ResizeObserver = vi.fn().mockImplementation(() => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
// Mock matchMedia for responsive components
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
// Mock localStorage and sessionStorage
const mockStorage = {
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
clear: vi.fn(),
length: 0,
key: vi.fn(),
};
Object.defineProperty(window, 'localStorage', {
writable: true,
value: mockStorage,
});
Object.defineProperty(window, 'sessionStorage', {
writable: true,
value: mockStorage,
});
});
// Clean up after each test
afterEach(() => {
cleanup();
vi.clearAllMocks();
});{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run",
"test:watch": "vitest --watch",
"test:coverage": "vitest run --coverage"
}
}Each package (@repo/shared, @repo/common) has:
{
"scripts": {
"test": "vitest",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage"
}
}Updated turbo.json includes test tasks:
{
"tasks": {
"test": {
"dependsOn": ["^test"],
"outputs": ["coverage/**"]
},
"test:run": {
"dependsOn": ["^test:run"],
"outputs": ["coverage/**"]
},
"test:coverage": {
"dependsOn": ["^test:coverage"],
"outputs": ["coverage/**"]
}
}
}// packages/shared/utils/__tests__/utils.test.ts
import { describe, expect, it } from 'vitest';
describe('Utils', () => {
it('should be a placeholder test', () => {
expect(true).toBe(true);
});
it('should format dates correctly', () => {
const testDate = new Date('2023-01-01T00:00:00.000Z');
expect(testDate.getFullYear()).toBe(2023);
});
it('should handle string operations', () => {
const testString = 'Hello World';
expect(testString.toLowerCase()).toBe('hello world');
expect(testString.split(' ')).toEqual(['Hello', 'World']);
});
});// packages/common/components/__tests__/footer.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import { Footer } from '../footer';
describe('Footer Component', () => {
it('should render footer with links', () => {
render(<Footer />);
expect(screen.getByText('Terms of Service')).toBeInTheDocument();
expect(screen.getByText('Privacy Policy')).toBeInTheDocument();
expect(screen.getByText('Help Center')).toBeInTheDocument();
});
it('should have proper link hrefs', () => {
render(<Footer />);
const termsLink = screen.getByRole('link', { name: 'Terms of Service' });
const privacyLink = screen.getByRole('link', { name: 'Privacy Policy' });
const faqLink = screen.getByRole('link', { name: 'Help Center' });
expect(termsLink).toHaveAttribute('href', '/terms');
expect(privacyLink).toHaveAttribute('href', '/privacy');
expect(faqLink).toHaveAttribute('href', '/faq');
});
it('should have appropriate styling classes', () => {
const { container } = render(<Footer />);
const footer = container.firstChild;
expect(footer).toHaveClass('flex', 'w-full', 'flex-col', 'items-center', 'justify-center');
});
});# Run all tests once
bun test:run
# Run tests in watch mode
bun test:watch
# Run tests with UI
bun test:ui
# Run tests with coverage
bun test:coverage
# Run tests for specific package
cd packages/shared && bun test
# Run specific test file
bun test packages/shared/utils/__tests__/utils.test.ts✓ packages/shared/utils/__tests__/utils.test.ts (3 tests) 3ms
✓ packages/common/components/__tests__/footer.test.tsx (3 tests) 29ms
Test Files 2 passed (2)
Tests 6 passed (6)
Start at 22:13:51
Duration 2.06s (transform 217ms, setup 431ms, collect 1.35s, tests 31ms, environment 295ms, prepare 144ms)
- Configured with proper path aliases for all packages
- Supports testing across package boundaries
- Turborepo integration for caching
- Testing Library integration
- Happy DOM environment for fast rendering
- jest-dom matchers for enhanced assertions
- Browser APIs (IntersectionObserver, ResizeObserver, matchMedia)
- Storage APIs (localStorage, sessionStorage)
- Easy to extend for additional mocks
- Fast test execution with Vite's transformation
- Coverage reporting with v8
- Watch mode for development
- Full TypeScript support out of the box
- Type-safe test writing
- IntelliSense support in test files
packages/
├── shared/
│ └── utils/
│ └── __tests__/
│ └── utils.test.ts
└── common/
└── components/
└── __tests__/
└── footer.test.tsx
- Use descriptive test names
- Group related tests with
describeblocks - Use
.test.tsor.spec.tsextensions
- Mock external dependencies at the test file level
- Use global mocks in setup file for common browser APIs
- Mock network requests appropriately
- Test user interactions, not implementation details
- Use accessible queries (getByRole, getByLabelText)
- Test component behavior, not internal state
- Fast Execution: Vitest is significantly faster than Jest
- Better DX: Excellent TypeScript support and debugging
- Modern Tooling: Uses Vite's transformation pipeline
- Monorepo Ready: Works seamlessly across packages
- Coverage Reports: Built-in coverage with v8
- Watch Mode: Fast re-runs during development
- Add more test examples for different types of components
- Set up GitHub Actions integration for CI/CD
- Configure coverage thresholds for quality gates
- Add visual regression testing with tools like Playwright
- Mock external APIs (database, auth, etc.) for integration tests
- TypeScript Errors: Ensure
@testing-library/jest-dom/vitestis imported - Module Resolution: Check alias configuration in
vitest.config.ts - Mock Issues: Verify global mocks are properly set up in
vitest.setup.ts - Coverage: Adjust exclude patterns in coverage configuration
- Use
test:runfor CI environments - Use
test:watchfor development - Configure appropriate test timeouts
- Exclude unnecessary files from coverage
Vitest has been successfully integrated into the VTChat monorepo, providing a modern, fast, and developer-friendly testing solution. The setup supports both utility function testing and React component testing across all packages, with proper TypeScript support and comprehensive mocking capabilities.