Skip to Content
⚠️Active Development Notice: TimeTiles is under active development. Information may be placeholder content or not up-to-date.
DevelopmentContributingTesting Guidelines

Testing Guidelines

Unit tests for logic, integration tests for workflows, E2E tests for user journeys. Use real implementations — only mock external paid APIs or rate-limited services.

Test Types

TypeLocationFrameworkSpeedWhat to test
Unittests/unit/Vitest< 100msPure functions, validation, parsing, formatting
Integrationtests/integration/Vitest + PostgreSQLsecondsAPI endpoints, job processing, access control
E2Etests/e2e/Playwright10-30sComplete user workflows in the browser

Running Tests

# From project root make test # All tests make test-ai # AI-friendly JSON output make test-ai FILTER=date.test # Single file (24-120x faster) make test-ai FILTER=tests/unit # Directory make test-e2e # Playwright E2E tests # From apps/web pnpm test # All tests pnpm test:unit # Unit only pnpm test:integration # Integration only pnpm test:e2e # E2E tests

Results are saved as timestamped JSON in apps/web/.test-results/.

Mocking Rules

Never Mock

  • Database operations — use test database
  • Payload CMS — use real Payload
  • Internal services — use actual implementations
  • File system — use temp directories
  • Job queues — use real handlers

Can Mock (Document Why)

  • External paid APIs (Google Maps) — costs and rate limits
  • Rate-limited services — avoid CI quotas
  • Network failures — test error handling
  • Timevi.setSystemTime() for date-dependent tests

Integration Test Setup

Use createIntegrationTestEnvironment() for tests that need a real database:

import { createIntegrationTestEnvironment, withCatalog, withDataset } from "@/tests/setup/integration-test-environment"; let testEnv: Awaited<ReturnType<typeof createIntegrationTestEnvironment>>; beforeAll(async () => { testEnv = await createIntegrationTestEnvironment(); }); afterAll(async () => { await testEnv.cleanup(); }); beforeEach(async () => { await testEnv.seedManager.truncate(); }); it("processes import file", async () => { const { catalog } = await withCatalog(testEnv); const { dataset } = await withDataset(testEnv, catalog.id); // Test with real database and Payload... });

Databases are isolated per worker and cleaned up automatically.

Unit Test Setup

Use factories and mocks for business logic:

import { vi } from "vitest"; import { createEvent } from "@/tests/setup/factories"; const mockPayload = { findByID: vi.fn(), create: vi.fn() }; it("validates event data", () => { const event = createEvent({ data: { title: "Test" } }); expect(validateEvent(event).valid).toBe(true); });

Test Credentials

Always use centralized test credentials to avoid hardcoded secrets:

import { TEST_CREDENTIALS, TEST_EMAILS } from "../constants/test-credentials"; const testUser = await payload.create({ collection: "users", data: { email: TEST_EMAILS.admin, password: TEST_CREDENTIALS.basic.password, role: "admin" }, });

Analyzing Failures

# Failed test names cat apps/web/.test-results/$(ls -t apps/web/.test-results/ | head -1) | jq '.testResults[] | select(.status=="failed") | .name' # Lint errors cat apps/web/.lint-results/$(ls -t apps/web/.lint-results/ | head -1) | jq '.[] | select(.errorCount > 0) | .filePath' # TypeScript errors cat apps/web/.typecheck-results/$(ls -t apps/web/.typecheck-results/ | head -1) | jq '.errors[] | {file, line, code, message}'
Last updated on