otpbox

OTPBox / Anleitungen / Playwright

Playwright

E-Mail-Verifizierung mit Playwright

Ein Registrierungsablauf, der mit "sieh in deinem Postfach nach dem Code" endet, ist end-to-end schwer zu testen, weil der Test ein Postfach braucht, das wirklich existiert und wirklich Post empfängt. OTPBox gibt Playwright eine echte Wegwerf-Adresse und eine Möglichkeit, auf den empfangenen Code zu warten — ohne den E-Mail-Anbieter zu mocken, ohne ein gemeinsam genutztes Test-Konto, um das sich andere Testläufe streiten.

Das Muster

Jeder Ansatz unten folgt denselben vier Schritten: ein echtes Postfach erstellen, dessen Adresse im Registrierungsformular verwenden, blockieren, bis der OTP oder der Verifizierungslink eintrifft, dann den Ablauf abschließen und aufräumen. Der Unterschied liegt nur darin, wie viel Boilerplate-Code du selbst schreibst.

Option 1: einfaches REST mit fetch

Keine Abhängigkeit außer Playwright selbst. Erzeuge einmalig einen kostenlosen Schlüssel (200 Anfragen/Monat, oder nutze einen Organisations-Schlüssel für CI — siehe Einen Schlüssel erhalten), und rufe dann die API direkt im Test auf:

import { test, expect } from '@playwright/test';

test('sign up with a real OTP', async ({ page }) => {
  const key = process.env.OTPBOX_KEY!;

  const inboxRes = await fetch('https://otpbox.io/api/v1/inboxes', {
    method: 'POST',
    headers: { authorization: `Bearer ${key}`, 'content-type': 'application/json' },
    body: '{}',
  });
  const inbox = await inboxRes.json(); // { id, address, domain, expiresAt, token }

  await page.goto('https://your-app.example.com/signup');
  await page.fill('[name="email"]', inbox.address);
  await page.click('button[type="submit"]');

  // Poll for the code (a real production test should back off / cap retries)
  let code: string | null = null;
  for (let i = 0; i < 15 && !code; i++) {
    await new Promise((r) => setTimeout(r, 1500));
    const msgs = await fetch(`https://otpbox.io/api/v1/inboxes/${inbox.id}/messages`, {
      headers: { authorization: `Bearer ${key}` },
    }).then((r) => r.json());
    code = msgs.messages[0]?.code ?? null;
  }

  await page.fill('[name="otp"]', code!);
  await page.click('button[type="submit"]');
  await expect(page.locator('text=Welcome')).toBeVisible();

  await fetch(`https://otpbox.io/api/v1/inboxes/${inbox.id}`, {
    method: 'DELETE',
    headers: { authorization: `Bearer ${key}` },
  });
});

Option 2: otpbox-sdk (empfohlen)

otpbox-sdk ist der offizielle TypeScript-Client, auf npm veröffentlicht, und erspart dir die manuelle Polling-Schleife dank waitForOtp():

npm install otpbox-sdk
import { test, expect } from '@playwright/test';
import { OTPBox } from 'otpbox-sdk';

test('sign up with a real OTP', async ({ page }) => {
  const client = new OTPBox({ apiKey: process.env.OTPBOX_KEY! });
  const inbox = await client.createInbox();

  try {
    await page.goto('https://your-app.example.com/signup');
    await page.fill('[name="email"]', inbox.address);
    await page.click('button[type="submit"]');

    const code = await client.waitForOtp(inbox.id, { timeoutMs: 20_000 });
    await page.fill('[name="otp"]', code!);
    await page.click('button[type="submit"]');
    await expect(page.locator('text=Welcome')).toBeVisible();
  } finally {
    await client.deleteInbox(inbox.id);
  }
});

waitForOtp() fragt getMessages() ab, bis eine nach Aufrufbeginn empfangene Nachricht einen extrahierten Code hat, oder bis das Zeitlimit abläuft (liefert bei Timeout null — code! oben ist in Ordnung, sobald du das in deinem eigenen Test geprüft hast, aber prüfe in echtem Code auf null). Es gibt außerdem waitForEmail(), falls du die ganze Nachricht willst, und getUsage(), falls eine Testsuite prüfen möchte, dass sie ihr Kontingent noch nicht aufgebraucht hat.

Option 3: otpbox-playwright-Fixture (in Arbeit)

Ein eigenes Playwright-Paket, otpbox-playwright, verpackt otpbox-sdk in eine eigene test-Fixture, sodass eine Spec nicht einmal selbst createInbox()/deleteInbox() aufrufen muss:

import { test, expect } from 'otpbox-playwright';

test('sign up with a real OTP', async ({ page, otpboxInbox }) => {
  await page.goto('https://your-app.example.com/signup');
  await page.fill('[name="email"]', otpboxInbox.address);
  await page.click('button[type="submit"]');

  const code = await otpboxInbox.waitForOtp();
  await page.fill('[name="otp"]', code);
  await page.click('button[type="submit"]');
  await expect(page.locator('text=Welcome')).toBeVisible();
  // no inbox.delete() to write - the fixture tears it down for you
});
Status: otpbox-playwright ist noch nicht auf npm veröffentlicht. Es ist echter, funktionierender Code im selben Monorepo wie otpbox-sdk, aber bis es als Paket erscheint, würdest du es per npm link gegen einen Klon des Repos installieren statt per npm install otpbox-playwright. Bis dahin ist Option 2 (otpbox-sdk direkt) der unterstützte Weg und liefert dasselbe waitForOtp()/waitForVerificationLink()-Verhalten mit ein paar zusätzlichen Zeilen Setup/Teardown.

Verifizierung per Link statt per Code

Manche Registrierungsabläufe verschicken einen Bestätigungslink statt eines sechsstelligen Codes. Dasselbe Postfach funktioniert dafür — nutze waitForEmail() und lies message.link (oder hole dir mit otpbox-sdk die vollständige Nachricht via getMessage(messageId) für link.url):

const message = await client.waitForEmail(inbox.id, { timeoutMs: 20_000 });
const full = await client.getMessage(message!.id);
await page.goto(full.link!.url);
await expect(page.locator('text=Verified')).toBeVisible();

Hinweise für CI

Nächste Schritte

Bereit, es an deiner eigenen App auszuprobieren? Kostenloses Konto erstellen

← Zurück zu OTPBox