Files
GearBox/docs/superpowers/specs/2026-04-03-testing-improvements-design.md

3.7 KiB

Testing Improvements Design

Date: 2026-04-03 Scope: Unit tests for new server code + Playwright E2E test setup with seeded database

Part 1: Unit/Integration Tests (Bun test runner)

tests/lib/params.test.ts

Tests for parseId helper in src/server/lib/params.ts:

  • Valid positive integers (1, 42, 999) return the number
  • Zero returns null
  • Negative numbers (-1, -100) return null
  • Decimals (1.5, 3.14) return null
  • Non-numeric strings ("abc", "", "hello") return null
  • NaN-producing values return null

tests/middleware/rateLimit.test.ts

Tests for rate limiter in src/server/middleware/rateLimit.ts:

  • First request passes through (200)
  • 5 requests succeed, 6th returns 429
  • 429 response includes Retry-After header
  • Different IPs tracked independently
  • After window expires, requests succeed again

Since the rate limiter uses a module-level Map, tests need to either:

  • Reset the store between tests (export a resetStore for testing), OR
  • Use unique paths/IPs per test to avoid interference

Recommended: export a _resetForTesting() function from rateLimit.ts that clears the store. Only used in tests.

tests/routes/params.test.ts

Route-level integration tests verifying 400 responses for invalid IDs:

  • GET /api/items/abc → 400
  • GET /api/items/-1 → 400
  • GET /api/items/0 → 400
  • DELETE /api/categories/notanumber → 400
  • GET /api/threads/abc → 400
  • GET /api/setups/abc → 400

Uses existing test app pattern with in-memory DB.

Part 2: Playwright E2E Setup

Installation

  • bun add -d @playwright/test
  • bunx playwright install chromium (only Chromium needed)

Configuration: playwright.config.ts

export default defineConfig({
  testDir: "./e2e",
  webServer: {
    command: "DATABASE_PATH=./e2e/test.db bun run dev:server",
    port: 3000,
    reuseExistingServer: !process.env.CI,
  },
  use: {
    baseURL: "http://localhost:3000",
  },
  projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
});

Database Seeding: e2e/seed.ts

Script that creates e2e/test.db with:

  • Run Drizzle migrations against the file
  • Seed data:
    • 1 user (username: "admin", password: "password123")
    • 3 categories: Shelter, Sleep System, Cook Kit
    • 6 items across categories with realistic weights/prices
    • 1 active thread with 3 candidates (with pros/cons, sort_order)
    • 1 resolved thread
    • 1 setup with 4 items (mixed classifications)
    • Settings: weightUnit=g, currency=USD, onboardingComplete=true

Run before E2E tests via e2e/global-setup.ts (Playwright globalSetup).

E2E Test Files

e2e/dashboard.spec.ts

  • Dashboard page loads
  • Summary cards show item count, weight, cost
  • Navigation links to collection work

e2e/collection.spec.ts

  • Gear tab renders items grouped by category
  • Search input filters items by name
  • Category filter dropdown works
  • Tab switching between gear/planning/setups

e2e/threads.spec.ts

  • Thread detail page loads with candidates
  • Comparison view toggle works (shows table)
  • Rank badges visible on candidates

e2e/auth.spec.ts

  • Login page renders
  • Login with valid credentials succeeds
  • Login with wrong password shows error
  • Rate limiting returns error after 5 attempts

e2e/error-boundary.spec.ts

  • App doesn't white-screen on unknown routes
  • Navigating to a non-existent thread/setup shows appropriate error

Scripts

Add to package.json:

  • "test:e2e": "bunx playwright test"
  • "test:e2e:ui": "bunx playwright test --ui" (for debugging)

Files to .gitignore

  • e2e/test.db
  • test-results/
  • playwright-report/

Commit Strategy

  1. Unit tests for parseId, rate limiter, route params
  2. Playwright setup (install, config, seed, global-setup)
  3. Playwright E2E test files