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.
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.
fetchSin 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}` },
});
});
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.
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
});
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.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();
OTPBOX_KEY como secreto del repositorio, nunca en el YAML del workflow — ver la sección CI/CD de la documentación para ejemplos con GitHub Actions, GitLab CI, Jenkins y CircleCI.Idempotency-Key en POST /api/v1/inboxes si tu CI reintenta llamadas de red inestables, para que un reintento no cree una segunda bandeja — ver Claves de idempotencia.finally para que una prueba fallida no la deje huérfana — aunque de todos modos el cron de expiración de 15 minutos la limpia.¿Listo para probarlo en tu propia app? Crea una cuenta gratuita
← Volver a OTPBox