Add testing strategy: Vitest for unit tests, Playwright for E2E
- Vitest: unit tests for packages, components, and app logic (jsdom env, @testing-library/react, co-located test files) - Playwright: E2E browser tests per app (journal.test.ts, planner.test.ts), auto-starts dev servers, scoped via testMatch - Sample unit test for types package - Smoke E2E tests for both apps - Updated CLAUDE.md with test commands and strategy Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4fac18c165
commit
40b9c425a0
11 changed files with 873 additions and 6 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -8,3 +8,6 @@ dist/
|
|||
.DS_Store
|
||||
*.log
|
||||
.crit.json
|
||||
e2e/results/
|
||||
test-results/
|
||||
playwright-report/
|
||||
|
|
|
|||
14
CLAUDE.md
14
CLAUDE.md
|
|
@ -58,9 +58,21 @@ turbo dev # Start both apps in dev mode
|
|||
turbo build # Build all packages and apps
|
||||
turbo typecheck # Type-check all packages
|
||||
turbo lint # Lint all packages
|
||||
turbo test # Run tests
|
||||
pnpm test # Run unit tests (vitest)
|
||||
pnpm test:watch # Run unit tests in watch mode
|
||||
pnpm test:e2e # Run E2E tests (playwright, requires dev servers)
|
||||
pnpm test:e2e:ui # Run E2E tests with Playwright UI
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
- **Unit tests** (Vitest + jsdom): For packages, components, utilities, and app logic.
|
||||
Place test files next to source: `foo.ts` → `foo.test.ts`.
|
||||
Uses `@testing-library/react` for component tests.
|
||||
- **E2E tests** (Playwright): For browser behavior across both apps.
|
||||
Tests live in `e2e/` at repo root. Scoped per app via `testMatch` in `playwright.config.ts`.
|
||||
Playwright auto-starts dev servers if not already running.
|
||||
|
||||
## Code Conventions
|
||||
|
||||
- All user-facing strings must use i18n (`useTranslation()` hook, never hardcode strings)
|
||||
|
|
|
|||
9
e2e/journal.test.ts
Normal file
9
e2e/journal.test.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Journal", () => {
|
||||
test("loads the home page", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page).toHaveTitle("trails.cool");
|
||||
await expect(page.getByText("Your outdoor activity journal")).toBeVisible();
|
||||
});
|
||||
});
|
||||
9
e2e/planner.test.ts
Normal file
9
e2e/planner.test.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
|
||||
test.describe("Planner", () => {
|
||||
test("loads the home page", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page).toHaveTitle("trails.cool Planner");
|
||||
await expect(page.getByText("Collaborative route planning")).toBeVisible();
|
||||
});
|
||||
});
|
||||
13
package.json
13
package.json
|
|
@ -11,19 +11,27 @@
|
|||
"dev": "turbo dev",
|
||||
"build": "turbo build",
|
||||
"lint": "turbo lint",
|
||||
"test": "turbo test",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:ui": "playwright test --ui",
|
||||
"typecheck": "turbo typecheck"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@react-router/dev": "^7.13.1",
|
||||
"@react-router/node": "^7.13.1",
|
||||
"@react-router/serve": "^7.13.1",
|
||||
"@tailwindcss/vite": "^4.2.2",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"eslint": "^10.1.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"jsdom": "^29.0.1",
|
||||
"playwright": "^1.58.2",
|
||||
"prettier": "^3.8.1",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
|
|
@ -31,6 +39,7 @@
|
|||
"tailwindcss": "^4.2.2",
|
||||
"turbo": "^2.8.20",
|
||||
"typescript-eslint": "^8.57.1",
|
||||
"vite": "catalog:"
|
||||
"vite": "catalog:",
|
||||
"vitest": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
packages/types/src/index.test.ts
Normal file
15
packages/types/src/index.test.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import type { Waypoint, Route } from "./index";
|
||||
|
||||
describe("types", () => {
|
||||
it("Waypoint type accepts valid data", () => {
|
||||
const waypoint: Waypoint = { lat: 52.52, lon: 13.405 };
|
||||
expect(waypoint.lat).toBe(52.52);
|
||||
expect(waypoint.lon).toBe(13.405);
|
||||
});
|
||||
|
||||
it("Waypoint supports isDayBreak flag", () => {
|
||||
const waypoint: Waypoint = { lat: 48.137, lon: 11.576, name: "Munich", isDayBreak: true };
|
||||
expect(waypoint.isDayBreak).toBe(true);
|
||||
});
|
||||
});
|
||||
45
playwright.config.ts
Normal file
45
playwright.config.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./e2e",
|
||||
outputDir: "./e2e/results",
|
||||
fullyParallel: true,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
reporter: process.env.CI ? "github" : "list",
|
||||
use: {
|
||||
baseURL: "http://localhost:3000",
|
||||
trace: "on-first-retry",
|
||||
screenshot: "only-on-failure",
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "journal",
|
||||
testMatch: "journal.test.ts",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
baseURL: "http://localhost:3000",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "planner",
|
||||
testMatch: "planner.test.ts",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
baseURL: "http://localhost:3001",
|
||||
},
|
||||
},
|
||||
],
|
||||
webServer: [
|
||||
{
|
||||
command: "pnpm --filter @trails-cool/journal dev",
|
||||
url: "http://localhost:3000",
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
{
|
||||
command: "pnpm --filter @trails-cool/planner dev",
|
||||
url: "http://localhost:3001",
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
],
|
||||
});
|
||||
756
pnpm-lock.yaml
generated
756
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -12,9 +12,6 @@
|
|||
"lint": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
|
|
|
|||
11
vitest.config.ts
Normal file
11
vitest.config.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "jsdom",
|
||||
include: ["packages/*/src/**/*.test.{ts,tsx}", "apps/*/app/**/*.test.{ts,tsx}"],
|
||||
exclude: ["**/e2e/**"],
|
||||
setupFiles: ["./vitest.setup.ts"],
|
||||
},
|
||||
});
|
||||
1
vitest.setup.ts
Normal file
1
vitest.setup.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
import "@testing-library/jest-dom/vitest";
|
||||
Loading…
Add table
Add a link
Reference in a new issue