Documentation Index
Fetch the complete documentation index at: https://dhanurgo.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Testing Guide
HostMetrics uses Vitest for unit and integration tests and Playwright for end-to-end browser tests.
Test Stack
| Tool | Purpose | Config File |
|---|
| Vitest | Unit and integration tests | vitest.config.ts |
| Playwright | End-to-end browser tests | playwright.config.ts |
| jsdom | DOM environment for Vitest | Set in Vitest config |
Directory Structure
src/__tests__/
├── unit/ # Unit tests for hooks, utils, pure functions
│ ├── useKPIs.test.ts
│ ├── calculations.test.ts
│ └── ...
├── integration/ # Integration tests (DB + component)
│ └── ...
└── mocks/
└── supabase.ts # Shared Supabase mock
e2e/
├── specs/ # Playwright test specs
│ ├── dashboard.spec.ts
│ ├── trips.spec.ts
│ └── ...
├── auth.setup.ts # Authentication setup
└── test-data.ts # Test fixture data
Running Tests
Unit Tests
# Run all unit tests in watch mode
npm run test
# Run all unit tests once (CI mode)
npm run test -- --run
# Run only unit tests
npm run test -- --run "src/__tests__/unit/"
# Run a specific test file
npm run test -- --run "src/__tests__/unit/useKPIs.test.ts"
# Run with coverage report
npm run test -- --run --coverage
End-to-End Tests
# Run all E2E tests
npx playwright test
# Run a specific spec file
npx playwright test e2e/specs/dashboard.spec.ts
# Run in headed mode (visible browser)
npx playwright test --headed
# Run in a specific browser
npx playwright test --project=chromium
# Open the Playwright UI
npx playwright test --ui
Vitest Configuration
Key settings from vitest.config.ts:
- Environment:
jsdom (simulates browser DOM)
- Coverage thresholds: 80% for branches, functions, lines, and statements
- Path aliases:
@/ maps to src/ (mirrors the app’s tsconfig)
- Setup files: Global test setup for mocks and environment
Test File Naming
| What you’re testing | File name pattern | Location |
|---|
| Component | ComponentName.test.tsx | src/__tests__/unit/ |
| Hook | hookName.test.ts | src/__tests__/unit/ |
| Utility function | utilName.test.ts | src/__tests__/unit/ |
| E2E user flow | feature.spec.ts | e2e/specs/ |
Mock Patterns
Mocking the Supabase Client
The shared Supabase mock lives at src/__tests__/mocks/supabase.ts. Use it in unit tests:
import { vi, describe, it, expect, beforeEach } from "vitest";
// Mock the Supabase client module
vi.mock("@/lib/db/_client", () => ({
getClient: () => mockSupabaseClient,
getCurrentUserId: () => "test-user-id",
}));
const mockSupabaseClient = {
from: vi.fn().mockReturnThis(),
select: vi.fn().mockReturnThis(),
eq: vi.fn().mockReturnThis(),
order: vi.fn().mockReturnThis(),
single: vi.fn().mockResolvedValue({
data: { id: "trip-1", reservation_id: "RES-001" },
error: null,
}),
};
Mocking Hooks in Component Tests
When testing components, mock the hooks they depend on:
import { vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { DashboardStats } from "@/components/dashboard/DashboardStats";
vi.mock("@/hooks/useKPIs", () => ({
useKPIs: () => ({
kpis: {
totalEarnings: 15000,
totalExpenses: 5000,
netProfit: 10000,
profitMargin: 66.7,
},
isLoading: false,
error: null,
}),
}));
describe("DashboardStats", () => {
it("renders KPI values", () => {
render(<DashboardStats />);
expect(screen.getByText("$15,000.00")).toBeInTheDocument();
});
});
Mocking Next.js Router
vi.mock("next/navigation", () => ({
useRouter: () => ({
push: vi.fn(),
replace: vi.fn(),
back: vi.fn(),
}),
usePathname: () => "/dashboard",
useSearchParams: () => new URLSearchParams(),
}));
Writing Unit Tests
Follow this structure for hook tests:
import { describe, it, expect, beforeEach, vi } from "vitest";
import { renderHook, waitFor } from "@testing-library/react";
import { useBookings } from "@/hooks/useBookings";
vi.mock("@/lib/db/_client", () => ({
getClient: () => mockClient,
getCurrentUserId: vi.fn().mockResolvedValue("test-user-id"),
}));
describe("useBookings", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("fetches bookings on mount", async () => {
const { result } = renderHook(() => useBookings());
await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});
expect(result.current.bookings).toHaveLength(3);
});
it("filters by status", async () => {
const { result } = renderHook(() =>
useBookings({ status: "completed" })
);
await waitFor(() => {
expect(result.current.bookings).toHaveLength(2);
});
});
});
Playwright E2E Tests
Browser Projects
Playwright is configured with 5 browser projects:
| Project | Browser | Viewport |
|---|
chromium | Chromium | Desktop |
firefox | Firefox | Desktop |
webkit | WebKit/Safari | Desktop |
mobile-chrome | Mobile Chrome | 390x844 |
mobile-safari | Mobile Safari | 390x844 |
Auth Setup
E2E tests authenticate before running specs using auth.setup.ts:
// e2e/auth.setup.ts
import { test as setup } from "@playwright/test";
setup("authenticate", async ({ page }) => {
await page.goto("/login");
await page.fill('[name="email"]', process.env.TEST_EMAIL!);
await page.fill('[name="password"]', process.env.TEST_PASSWORD!);
await page.click('button[type="submit"]');
await page.waitForURL("/dashboard");
// Save auth state for reuse
await page.context().storageState({ path: "e2e/.auth/state.json" });
});
Writing E2E Specs
import { test, expect } from "@playwright/test";
test.describe("Dashboard", () => {
test("displays KPI cards", async ({ page }) => {
await page.goto("/dashboard");
await expect(page.getByText("Total Earnings")).toBeVisible();
await expect(page.getByText("Net Profit")).toBeVisible();
});
test("navigates to trip details", async ({ page }) => {
await page.goto("/bookings");
await page.click("text=RES-001");
await expect(page).toHaveURL(/\/bookings\//);
});
});
Pre-Commit Test Commands
Before creating a PR, always run:
# Unit tests (must pass)
npm run test -- --run
# Or specifically unit tests only
npm run test -- --run "src/__tests__/unit/"
See the PR Checklist for the full verification workflow.