Development

Testing

Unit testing with Vitest, shared test helpers, mocks, and coverage targets

Overview

The monorepo uses Vitest for unit testing with shared configuration and helpers from the @hn-monorepo/testing package. E2E testing is covered separately in the E2E Testing guide.

Quick Reference

CommandDescription
bun testRun all unit tests
bun test:watchRun tests in watch mode
bun test:coverageRun tests with coverage report
turbo test --filter=<app>Run tests for a specific app
bun test:e2eRun all E2E tests (Playwright)

Shared Testing Package

The @hn-monorepo/testing package (packages/testing/) provides:

  • Shared Vitest configuration
  • Test fixtures and helpers
  • Mock factories for common dependencies
  • Playwright configuration factory (for E2E)

Shared Mocks

Pre-built mocks are available for commonly used dependencies:

MockPurpose
Next.js mocksnext/navigation, next/image, next/headers
Convex mocksconvex/react hooks, query/mutation wrappers
i18n mocksnext-intl translation functions

Import mocks from the testing package:

import { mockConvex, mockNavigation, mockI18n } from "@hn-monorepo/testing";

Writing Unit Tests

Test File Location

Unit tests live alongside the code they test or in a tests/ directory:

apps/<app>/
├── src/
│   └── components/
│       └── feature/
│           ├── feature.tsx
│           └── feature.test.ts    # Co-located test
├── tests/                          # Or in a tests directory
│   └── feature.test.ts

Basic Test Structure

import { describe, expect, it } from "vitest";

describe("FeatureName", () => {
  it("should handle the expected case", () => {
    const result = myFunction(input);
    expect(result).toBe(expected);
  });
});

Testing Guidelines

When to Write Tests

ChangeTest Requirement
Bug fixRegression test required
New user flowE2E test recommended
Convex business logicUnit test recommended
Shared package changesUnit test required
UI componentTest if complex logic involved

Coverage Targets

  • Shared packages (packages/): Target 60%+ coverage
  • App code (apps/): Coverage not enforced, but tests recommended for business logic
  • Convex functions: Unit tests recommended for business logic in convex/model/ layer

Convex Business Logic

Keep public API functions thin and delegate to a model layer. Test the model layer:

// convex/model/vocabulary.ts — business logic (testable)
export function validateVocabularySet(data: VocabularySetInput) {
  // ...
}

// convex/functions/vocabulary.ts — thin API layer
export const create = mutation({
  handler: async (ctx, args) => {
    const validated = validateVocabularySet(args);
    return ctx.db.insert("vocabularySets", validated);
  },
});

Page Object Model

For complex E2E flows, use the page object model pattern with selectors defined in e2e/selectors/:

// e2e/selectors/login.ts
export const loginSelectors = {
  emailInput: '[data-testid="email-input"]',
  passwordInput: '[data-testid="password-input"]',
  submitButton: '[data-testid="login-submit"]',
};

CI Integration

Unit tests run automatically in the CI pipeline:

  • Job: test in .github/workflows/ci.yml
  • Timeout: 15 minutes
  • Trigger: Runs on all PRs and pushes to master
  • Scope: Tests run for changed packages only (detected via git diff)

Test failures block PR merge.

HanseNexus 2026