QA / E2E guide

Open-source QA flows — temporary inbox per test run, OTP and magic links without a shared test@company.com. Source on GitHub.

Problems we solve

PainSolution
Shared inbox races in CIlabel per run / worker
Long waits in testsPOST /v1/inboxes/open
Wrong email pickedsubjectContains + allowlist
Parse HTML for OTPverification.otp, primaryLink
Debug failed runGET /v1/inboxes?label=...

30-minute pilot

Fastest: copy qa-pilot-starter (simulate in CI · staging via APP_SIGNUP_URL) · Cypress starter. Guide: QA-PILOT.md · validate with npm run wizard:qa-pilot.

CI quick start

export MAILAGENT_API_URL=https://api.webmailagent.com
export MAILAGENT_API_KEY=your_key
export RUN_ID="${GITHUB_RUN_ID:-local}"

curl -sS -X POST "$MAILAGENT_API_URL/v1/inboxes/open" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"label\": \"ci-$RUN_ID\",
    \"service\": \"auth0\",
    \"subjectContains\": \"verification\",
    \"timeoutSeconds\": 120
  }"

Recommended E2E order

  1. POST /v1/inboxes with label → get address
  2. Fill signup form on staging
  3. GET /v1/inboxes/:id/wait?subjectContains=... then /extract

Playwright / @mailagent/qa

npm install @mailagent/qa@latest
import { createMailAgentQa, MailAgentQa } from "@mailagent/qa";

const mail = createMailAgentQa(); // MAILAGENT_API_URL + MAILAGENT_API_KEY
const inbox = await mail.createInbox({
  label: MailAgentQa.ciLabel("pw"),
  service: "auth0",
});
// page.fill(inbox.address) → submit
const v = await mail.waitForVerification(inbox.id, {
  subjectContains: "verify",
  messageIndex: 0, // 1 = second matching email
});
// callbackUrl flows: await mail.waitForCallback(inbox.id, { since: inbox.createdAt });

Fixture with auto-delete: examples/playwright/mailagent.fixture.ts

QA fields

FieldWherePurpose
labelcreate, openCI run / worker id
subjectContainsopen, waitFilter by subject
messageIndexopen, wait, verify0=newest match, 1=second, …
callbackUrlcreate, openHTTPS webhook on new mail
deleteAfteropenfalse to keep inbox for debug

Cypress

import { createMailAgentCypressTasks } from "@mailagent/qa/cypress";
// setupNodeEvents: on("task", createMailAgentCypressTasks())

Example: examples/cypress

CI secrets

Use a scoped key per pipeline — labelPrefix: "ci-" so a leaked secret cannot touch other runs or team admin. Scoped keys guide →

Debug after failure

Open Debug UI with ?inbox=INBOX_ID from @mailagent/qa errors.

GET /v1/inboxes?label=ci-12345
GET /v1/inboxes/:id/messages
GET /v1/inboxes/:id/callbacks

More guides

QA roadmap (P0–P2): docs/QA-ROADMAP.md

Debug failed runs in the Debug console. Pilot access: hello@webmailagent.com.