otpbox

OTPBox / Guías / Playwright

Playwright

Verificación de email con Playwright

Un flujo de registro que termina en "revisa tu email para ver el código" es difícil de probar de principio a fin, porque la prueba necesita una bandeja de entrada que exista de verdad y reciba correo de verdad. OTPBox le da a Playwright una dirección desechable real y una forma de esperar el código que recibe — sin simular el proveedor de correo, sin una cuenta de prueba compartida con la que choquen otras ejecuciones.

El patrón

Cada enfoque de abajo sigue los mismos cuatro pasos: crear una bandeja real, usar su dirección en el formulario de registro, bloquear hasta que llegue el OTP o el enlace de verificación, y luego terminar el flujo y limpiar. La diferencia está solo en cuánto código repetitivo escribes tú mismo.

Opción 1: REST simple con fetch

Sin ninguna dependencia además de Playwright. Genera una clave gratuita una vez (200 solicitudes/mes, o usa una clave de organización para CI — ver Obtener una clave), y luego llama a la API directamente dentro de la prueba:

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}` },
  });
});

Opción 2: otpbox-sdk (recomendado)

otpbox-sdk es el cliente oficial en TypeScript, publicado en npm, y elimina el bucle de sondeo manual con 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() sondea getMessages() hasta que un mensaje recibido después de iniciar la llamada tenga un código extraído, o hasta que se agote el tiempo (devuelve null si se agota — code! arriba está bien una vez que lo has verificado en tu propia prueba, pero comprueba null en código real). También existe waitForEmail() si quieres el mensaje completo, y getUsage() si una suite quiere comprobar que no ha agotado su cuota.

Opción 3: fixture otpbox-playwright (en desarrollo)

Un paquete dedicado de Playwright, otpbox-playwright, envuelve otpbox-sdk en un test fixture personalizado para que una spec ni siquiera tenga que llamar ella misma a createInbox()/deleteInbox():

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
});
Estado: otpbox-playwright aún no está publicado en npm. Es código real y funcional en el mismo monorepo que otpbox-sdk, pero hasta que salga como paquete lo instalarías con npm link contra un clon del repositorio en lugar de npm install otpbox-playwright. Mientras tanto, la Opción 2 (otpbox-sdk directamente) es el camino soportado y te da el mismo comportamiento de waitForOtp()/waitForVerificationLink() con unas pocas líneas extra de configuración.

Verificar con un enlace en lugar de un código

Algunos flujos de registro envían un enlace de confirmación en lugar de un código de 6 dígitos. La misma bandeja funciona — usa waitForEmail() y lee message.link (o, con otpbox-sdk, obtén el mensaje completo con getMessage(messageId) para acceder a 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();

Notas para CI

Próximos pasos

¿Listo para probarlo en tu propia app? Crea una cuenta gratuita

← Volver a OTPBox