Loading...
Loading...
Testing strategy and workflow. Tests run in parallel with isolated data per suite. Prioritize Playwright for UI, integration tests for APIs, unit tests for logic.
npx skill4agent add andrelandgraf/fullstackrecipes using-testsbun run test # All tests with isolated Neon branch
bun run test:playwright # Browser tests only
bun run test:integration # Integration tests only
bun run test:unit # Unit tests onlysrc/
├── lib/
│ ├── common/
│ │ ├── assert.ts
│ │ └── assert.test.ts # Unit test (co-located)
│ └── config/
│ ├── schema.ts
│ └── schema.test.ts # Unit test (co-located)
tests/
├── integration/
│ ├── llms.test.ts # Integration test
│ ├── r.test.ts
│ ├── mcp/
│ │ └── route.test.ts
│ └── recipes/
│ └── [slug]/
│ └── route.test.ts
└── playwright/
├── auth.spec.ts # Playwright test
├── chat.spec.ts
├── home.spec.ts
└── lib/
└── test-user.ts # Playwright-specific helperstests/playwright/{feature}.spec.tsimport { test, expect } from "@playwright/test";
test.describe("Feature Name", () => {
test("should do expected behavior", async ({ page }) => {
await page.goto("/feature");
// Test implementation
});
});tests/integration/{feature}.test.tsimport { describe, it, expect } from "bun:test";
import { GET } from "@/app/api/feature/route";
describe("GET /api/feature", () => {
it("should return expected response", async () => {
const response = await GET();
expect(response.status).toBe(200);
const data = await response.json();
expect(data.value).toBeDefined();
});
});src/lib/{domain}/{file}.test.tsimport { describe, it, expect } from "bun:test";
import { myFunction } from "./my-file";
describe("myFunction", () => {
it("should do expected behavior", () => {
expect(myFunction()).toBe("expected");
});
});// auth.spec.ts - uses auth-specific test user
const testUser = await createTestUser({
email: `auth-test-${uuid}@example.com`,
});
// chat.spec.ts - uses chat-specific test user
const testUser = await createTestUser({
email: `chat-test-${uuid}@example.com`,
});test("should redirect unauthenticated user", async ({ page }) => {
await page.goto("/protected-page");
await expect(page).toHaveURL(/sign-in/);
});test("should show 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,
});
});import { GET } from "@/app/api/endpoint/route";
it("should return 200 for valid request", async () => {
const response = await GET();
expect(response.status).toBe(200);
});bunx playwright test --headed # Watch browser
bunx playwright test --debug # Step through test
bunx playwright show-report # View HTML reportbun test --only "test name" # Run single test
bun test --watch # Re-run on changestest-results/