Testing
The frontend uses Vitest as its test framework. There are no browser-based E2E tools (no Playwright or Cypress) — "E2E" here means end-to-end API integration tests that call live external services.
Setup
Framework: Vitest ^2.1.8
Config: vitest.config.ts
defineConfig({
test: {
globals: true,
environment: 'node',
testTimeout: 30000, // 30s for API calls
hookTimeout: 30000,
teardownTimeout: 30000,
},
resolve: {
alias: { '@': './src' },
},
})
Running Tests
| Command | What it does |
|---|---|
npm test |
Watch mode (re-runs on file changes) |
npm run test:run |
Single run |
npm run test:utc |
Single run with TZ=UTC (for date-sensitive tests) |
npm run test:yearn |
Yearn-specific test script |
npm run test:yearn:watch |
Watch mode for Yearn tests only |
Test Categories
Unit Tests (src/v2/positions/__tests__/)
Mock-based tests that isolate logic from external dependencies. These use vi.mock() to replace Prisma, Zerion, and other services.
| File | What it tests |
|---|---|
helpers.test.ts |
FIFO realized PnL calculations, reward flows |
rows.test.ts |
Complex position row building, protocol-specific values |
summary.test.ts |
Position summaries, Zerion vs Valuation preference, aggregation |
wallet-cards.test.ts |
Wallet card building, EVM wallet filtering |
stacked-chart.test.ts |
Stacked chart data generation |
total-chart.test.ts |
Total portfolio chart data, valuation bucketing |
Protocol Integration Tests (src/lib/defi/)
Live API tests that call real external services. These have longer timeouts (10–60 seconds) and can fail if external APIs are down.
| File | What it tests |
|---|---|
__tests__/positions.e2e.test.ts |
Cross-protocol position fetching: Aave supplies, Uniswap V2/V3 positions, Yearn vault balances, Morpho positions |
morpho/data.test.ts |
MetaMorpho vault data, multi-chain support, performance |
uniswap/v2/data.test.ts |
Uniswap V2 pair data, pagination, user positions |
uniswap/v3/data.test.ts |
Uniswap V3 pool data, positions, APY calculations |
yearn/__tests__/sdk.test.ts |
Yearn vault listing, user balances, on-chain functions |
trends/__tests__/service.test.ts |
DeFi trends: blue-chip pools, stablecoin pools, high-APR pools |
CI Integration
Tests run automatically in the build.yml GitHub Actions workflow on every push to main/staging and on all pull requests.
- name: Unit tests (vitest src/v2)
if: ${{ github.event_name != 'workflow_call' }}
run: npm run test:run -- src/v2
Important: CI only runs src/v2/ tests (the unit tests). Protocol integration tests are not run in CI because they depend on live external APIs and have long timeouts.
Testing Patterns
Mocking: Unit tests use vi.hoisted() + vi.mock() to replace modules before import:
const prismaMock = vi.hoisted(() => ({
nmt_position: { findMany: vi.fn() },
nmt_position_valuation: { findMany: vi.fn() },
}));
vi.mock("server-only", () => ({}));
vi.mock("@/lib/prisma/client", () => ({
default: prismaMock,
}));
Multi-chain testing: Integration tests run against multiple chains (Mainnet, Optimism, Base, Arbitrum) and gracefully skip unsupported chains with console.warn.