Skip to main content

playwright-api-tests

Add or extend Playwright HTTP API specs under apps/web/playwright/api using the shared api/workspace fixtures and Bearer auth from globalSetup. Use when writing Playwright API tests, e2e API specs, or testing /api routes with the api project (not Vitest under apps/web/tests, not browser UI e2e).

Quellinformationen

Repository
dubinc/dub
Letzte Quellaktivität
15. August 2026 um 15:12
Erkannte Sprache von SKILL.md
Englisch
Sterne
24.873
Forks
3.319

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
playwright-api-tests
description
Add or extend Playwright HTTP API specs under apps/web/playwright/api using the shared api/workspace fixtures and Bearer auth from globalSetup. Use when writing Playwright API tests, e2e API specs, or testing /api routes with the api project (not Vitest under apps/web/tests, not browser UI e2e).
# Playwright API tests HTTP API coverage lives in `apps/web/playwright/api/<resource>/*.spec.ts`. Auth is seeded once by Playwright `globalSetup` (`apps/web/global-setup.ts` → `setup-test-workspace.ts`) into `playwright/.auth/api.json`. Specs use the custom `test` from `../fixtures` — not `@playwright/test` directly for `test`. Do **not** put these under `apps/web/tests/` (Vitest) or under `playwright/partners` / `playwright/workspaces` (browser e2e). ## Before writing 1. Find the route handler and response shape (Zod schemas under `apps/web/lib/zod`, types under `@/lib/types` or Prisma). 2. Prefer mirroring an existing nearby resource folder: `tags/`, `folders/`, `customers/`, `workspaces/`. 3. Confirm the endpoint is workspace-scoped Bearer API (most `/api/*` routes). The fixture already sends `Authorization: Bearer …`. ## What to cover Before considering an endpoint covered, ask: - What happens with valid input? - What happens with invalid input? - Who is allowed to call it? - Can another workspace access the resource? - What happens when the resource does not exist? - What happens with duplicate data? - What happens at boundary values? - What state does the operation leave behind? - Can the operation be safely retried? - Does pagination/filtering/sorting behave correctly? - What happens when multiple requests happen concurrently? - Are errors returned in the documented format? Not every endpoint needs a test for every item. The goal is to cover the actual failure modes and API contract, not to maximize the number of tests. ## File layout ``` apps/web/playwright/api/ ├── fixtures.ts # api + workspace fixtures (reuse; don't duplicate) ├── setup-test-workspace.ts # globalSetup seed only — extend only if auth/workspace setup must change └── <resource>/ ├── <resource>.spec.ts └── <resource>-pagination.spec.ts # optional split for heavy pagination suites ``` One folder per resource. Name files `kebab-case.spec.ts` (`tags/tags.spec.ts`, `customers/customers-pagination.spec.ts`). ## Spec template ```ts import { expect } from "@playwright/test"; import { randomName } from "../../utils"; import { test, type ApiClient } from "../fixtures"; async function createThing( api: ApiClient, overrides: Record<string, unknown> = {}, ) { return api.post<YourType>("/api/things", { name: randomName("thing"), ...overrides, }); } async function deleteThing(api: ApiClient, id: string | undefined) { if (!id) return; await api.delete(`/api/things/${id}`); } test("POST /things", async ({ api }) => { let id: string | undefined; try { const body = { name: randomName("thing") }; const { status, data } = await api.post<YourType>("/api/things", body); id = data.id; expect(status).toEqual(201); expect(data).toStrictEqual({ id: expect.any(String), ...body, // stable nulls / defaults from the API }); } finally { await deleteThing(api, id); } }); ``` ## Required conventions | Rule | Detail | | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | Import `test` from `../fixtures` | Provides `api`, `workspace`, and `program` | | Serial only when tests share state | API project is `fullyParallel: true`. Do not add `mode: "parallel"`. Use `test.describe.configure({ mode: "serial" })` only when tests in a file/describe share state (e.g. domains, seeded pagination) | | Cleanup in `finally` | Create → assert → always delete created rows | | Unique names/ids | Use `randomName` / `randomCustomer` / `randomPartnerEmail` from `../../utils` — never fixed colliding names | | Assert status + body | Prefer `toStrictEqual` / `toEqual` on full shapes; use `expect.any(String)` for ids/timestamps | | Default shape once | Happy-path POST owns the full default resource (and nested) shape. Variant tests assert only what they change | | HTTP contract only | Assert status + JSON. Do not poll/sleep for `waitUntil`, R2, or other background jobs — CI has no `STORAGE_*` | | Error responses | Match `{ error: { code, message, doc_url } }` exactly | | Typed generics | `api.get<T>`, `api.post<T>`, etc. | | Seeded fixtures | `{ workspace }`, `{ program }` (`id`, `defaultGroupId`), `TEST_WORKSPACE` — not Vitest `E2E_*` constants | ### Fixtures (`api` / `workspace` / `program`) - `api.get/post/patch/delete(url, data?)` → `{ status, data }` (JSON parsed). - Paths are app-relative (`/api/...`); `baseURL` is `http://localhost:8888`. - `workspace.id` / `workspace.slug` and `program.id` / `program.defaultGroupId` come from `.auth/api.json`. - Seeded program domain/url: `TEST_WORKSPACE.program` in `setup-test-workspace.ts`. ### Helpers - Shared randomizers / sort assertions: `apps/web/playwright/utils.ts`. - Local `createX` / `deleteX` helpers in the spec file when the same setup repeats. - Once `createX` exists, use it for setup **and** follow-up creates (pass overrides for identity fields that must stay stable, e.g. `email`). Happy-path tests may still inline `api.post` so the asserted body is local. - Prisma direct seed is allowed for bulk fixtures (see `customers/customers-pagination.spec.ts`); still clean up in `finally` / after hooks. No DELETE route → Prisma/`conn` cleanup is fine (`partners/partners.spec.ts`). ### Error / table-driven cases ```ts const errorCases = [ { name: "POST /things – missing name", body: {}, expected: { status: 422, data: { error: { code: "unprocessable_entity", message: "…", doc_url: "https://dub.co/docs/api-reference/errors#unprocessable-entity", }, }, }, }, ]; for (const { name, body, expected } of errorCases) { test(name, async ({ api }) => { expect(await api.post("/api/things", body)).toEqual(expected); }); } ``` Optional: validate richer payloads with Zod (`.parse(...)`) like `workspaces/workspaces.spec.ts` / enriched customer fields. ## Migrating Vitest API tests When converting `apps/web/tests/<resource>/*.test.ts`: 1. Add or extend `playwright/api/<resource>/<resource>.spec.ts` — do not leave a parallel Vitest HTTP spec. 2. Map `IntegrationHarness` / `http.post({ path })` → `api` fixture (`/api/...` paths). 3. Replace `E2E_*` / `E2E_PARTNER_GROUP` with `{ workspace }`, `{ program }`, and `TEST_WORKSPACE`. 4. Delete the Vitest file after the Playwright spec covers its cases. ## Do not - Call `setupTestWorkspace` from specs (only `globalSetup`). - Import browser `page` / storage-state auth for API specs. - Commit secrets or change the fixed Playwright token unless intentionally rotating local/CI test auth. - Skip cleanup (parallel API project will leak / flake). - Run `pnpm build` after adding tests. - Poll or `setTimeout` for R2/storage/`waitUntil` side effects (no `STORAGE_*` in CI). Assert the immediate JSON body instead (e.g. foreign `image` URLs stay `null` on create). - Copy Vitest `IntegrationHarness` or `E2E_*` constants into Playwright specs. ## Run Dev server on port 8888 (local), then: ```sh pnpm --filter web test:e2e --project=api pnpm --filter web test:e2e --project=api playwright/api/tags/tags.spec.ts pnpm --filter web test:e2e --project=api playwright/api/tags/tags.spec.ts -g "POST /tags" ``` `-g` / `--grep` matches the test title (regex). Combine with a file path to narrow further. In CI, `playwright.config.ts` starts `pnpm start -p 8888` via `webServer`. More context: `apps/web/playwright/README.md` (API section).
Auf GitHub ansehen