From andrelandgraf-fullstackrecipes
Guides test type selection (Playwright > integration > unit) and runs tests against isolated Neon branches. Use when adding, running, or debugging tests.
How this skill is triggered — by the user, by Claude, or both
Slash command
/andrelandgraf-fullstackrecipes:testing-best-practicesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Choose the right test type, isolate data per suite, and run against a disposable Neon branch.
Choose the right test type, isolate data per suite, and run against a disposable Neon branch.
Complete these setup recipes first:
Ask "how would a user verify this works?" and pick the highest applicable tier:
All tests run against an isolated, schema-only Neon branch that auto-deletes after 1 hour.
bun run test # all tests
bun run test:playwright # browser only
bun run test:integration # integration only
bun run test:unit # unit only
Unit tests are co-located; Playwright and integration tests live under tests/.
src/lib/<domain>/<file>.test.ts # unit (co-located)
tests/integration/<feature>.test.ts
tests/playwright/<feature>.spec.ts
tests/playwright/lib/ # Playwright helpers
Playwright spec:
import { test, expect } from "@playwright/test";
test.describe("Feature Name", () => {
test("should do expected behavior", async ({ page }) => {
await page.goto("/feature");
});
});
Integration — import the route handler directly instead of going over HTTP:
import { describe, it, expect } from "bun:test";
import { GET } from "@/app/api/feature/route";
describe("GET /api/feature", () => {
it("returns expected response", async () => {
const response = await GET();
expect(response.status).toBe(200);
const data = await response.json();
expect(data.value).toBeDefined();
});
});
Unit (co-located):
import { describe, it, expect } from "bun:test";
import { myFunction } from "./my-file";
describe("myFunction", () => {
it("returns expected value", () => {
expect(myFunction()).toBe("expected");
});
});
Tests run in parallel against the shared branch, so each suite must own its data. Generate unique users per spec (e.g. auth-test-${uuid}@example.com), avoid shared resources, and rely on the branch TTL for cleanup — never assume data from another test exists.
const testUser = await createTestUser({
email: `auth-test-${uuid}@example.com`,
});
// Protected route (Playwright)
test("redirects unauthenticated user", async ({ page }) => {
await page.goto("/protected-page");
await expect(page).toHaveURL(/sign-in/);
});
// Error state (Playwright)
test("shows error for invalid input", async ({ page }) => {
await page.goto("/form");
await page.getByRole("button", { name: /submit/i }).click();
await expect(page.getByText(/error|required/i)).toBeVisible({
timeout: 5000,
});
});
bunx playwright test --headed # watch the browser
bunx playwright test --debug # step through
bunx playwright show-report # HTML report
bun test --only "test name" # run a single test
bun test --watch # re-run on change
Failed Playwright runs save screenshots and traces to test-results/ — check there when CI fails.
npx claudepluginhub joshuarweaver/cascade-code-general-misc-3 --plugin andrelandgraf-fullstackrecipesGuides web app testing strategy: test pyramid (unit/integration/component/E2E), framework selection (Vitest/Playwright/Jest), coverage rules, mocking boundaries, and execution.
Sets up Playwright test projects with installation commands, basic/advanced playwright.config.ts configurations, multi-browser projects, and webServer for scalable test suites.
Provides testing pyramid, unit patterns (AAA, isolation, parameterized, edge cases), and React Testing Library for component tests. Use when writing tests or setting up testing infrastructure.