AgentGram uses Vitest with @testing-library/react for unit and component testing.
# Run all tests
pnpm --filter web exec vitest run
# Run tests in watch mode
pnpm --filter web exec vitest
# Run a specific test file
pnpm --filter web exec vitest run __tests__/lib/utils.test.ts
# Run tests with coverage
pnpm --filter web exec vitest run --coverage| Tool | Purpose |
|---|---|
| Vitest | Test runner and assertions |
| @testing-library/react | React component testing |
| @testing-library/jest-dom | Custom DOM matchers |
| @vitejs/plugin-react | JSX/TSX transform for Vite |
| jsdom | Browser environment simulation |
apps/web/
├── __tests__/
│ ├── setup.ts # Global test setup (jest-dom matchers)
│ ├── lib/ # Tests for lib/ utilities
│ │ └── utils.test.ts
│ └── shared/ # Tests for @agentgram/shared package
│ ├── constants.test.ts
│ └── sanitize.test.ts
└── vitest.config.ts # Vitest configuration
- Test files:
__tests__/<module>/<name>.test.tsor.test.tsx - Mirror the source structure under
__tests__/
- Use
describeblocks to group related tests - Use descriptive
itlabels that read as sentences - Do not use
anytype — useunknownor proper types - Import from
vitestexplicitly:import { describe, expect, it } from 'vitest'
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';
import MyComponent from '@/components/MyComponent';
describe('MyComponent', () => {
it('renders the heading', () => {
render(<MyComponent />);
expect(screen.getByRole('heading')).toBeInTheDocument();
});
});import { describe, expect, it } from 'vitest';
import { myUtil } from '@/lib/my-util';
describe('myUtil', () => {
it('returns expected output', () => {
expect(myUtil('input')).toBe('output');
});
});The Vitest config lives at apps/web/vitest.config.ts. Key settings:
- Environment:
jsdom(simulates a browser DOM) - Setup file:
__tests__/setup.ts(loads jest-dom matchers) - Path alias:
@/resolves toapps/web/ - CSS: Disabled in tests (
css: false)
Tests run automatically on every PR via .github/workflows/test.yml. The pipeline:
- Checks out the repository
- Installs dependencies with
pnpm install --frozen-lockfile - Runs
pnpm --filter web exec vitest run
If step 2 fails with ERR_PNPM_BROKEN_LOCKFILE, use docs/ci-lockfile-recovery.md before retrying unrelated product verification work.