Prueba la verificación de correo en Playwright con saci.cc
Para probar un registro que envía un código o un enlace por correo, tu test necesita una bandeja de entrada real que pueda leer. Con el paquete saci.cc de npm, el test crea una dirección, se registra con ella, espera el correo y recibe el código o el enlace como un string. Solo hace falta una clave de API gratis y unas pocas líneas de código, y funciona igual en Playwright, Cypress o cualquier script de Node.
Por qué una bandeja real
Un atajo común es reemplazar el envío de correos por un mock en los tests. Eso demuestra que tu código llama al servicio de envío, pero no que el correo sale, llega a una bandeja y trae un código que funciona. Una bandeja real cierra ese hueco: el test hace exactamente lo que hace un usuario, del formulario al código.
Consigue una clave de API
La API pide una clave, y es gratis:
- Entra en la página de tu cuenta con GitHub. La cuenta de GitHub tiene que tener al menos 30 días.
- Crea una clave. Empieza con
saci_y solo se muestra una vez, así que cópiala en ese momento. - Cada cuenta puede tener hasta 5 claves, por ejemplo una para tu ordenador y otra para el CI. Puedes revocar cualquiera.
El cliente lee la clave de la variable de entorno SACI_API_KEY, así que no queda en tu código:
npm install --save-dev saci.cc export SACI_API_KEY=saci_...
En el CI, guarda la clave como secret y pásala al paso de los tests. En GitHub Actions:
- run: npx playwright test
env:
SACI_API_KEY: ${{ secrets.SACI_API_KEY }}Un test de registro con código de verificación
createInbox() crea una dirección aleatoria nueva. inbox.waitForCode() espera a que llegue un correo con código de verificación y devuelve el código:
import { test, expect } from "@playwright/test";
import { createInbox } from "saci.cc";
test("registro con código de verificación", async ({ page }) => {
test.setTimeout(90_000);
const inbox = await createInbox();
await page.goto("https://tu-app.com/registro");
await page.getByLabel("Correo").fill(inbox.address);
await page.getByLabel("Contraseña").fill("caballo correcto batería grapa");
await page.getByRole("button", { name: "Crear cuenta" }).click();
const code = await inbox.waitForCode({ from: "tu-app.com" });
await page.getByLabel("Código de verificación").fill(code);
await page.getByRole("button", { name: "Verificar" }).click();
await expect(page.getByText("Bienvenida")).toBeVisible();
});El filtro from es opcional. Solo acepta correos cuyo remitente contiene ese texto, sin distinguir mayúsculas, lo que ayuda cuando la bandeja recibe más de un correo.
Sube el timeout del test
Playwright da 30 segundos a cada test por defecto. El correo suele llegar en pocos segundos, pero un proveedor lento puede tardar más, y el cliente espera hasta 60 segundos antes de lanzar SaciTimeoutError. Llama a test.setTimeout(90_000) en los tests que esperan un correo, o sube el timeout en la configuración de Playwright. Para cambiar cuánto espera el propio cliente, pasa timeout en milisegundos: inbox.waitForCode({ timeout: 30_000 }).
El cliente no consulta la bandeja sin parar. Le pide a la API que mantenga la solicitud abierta hasta que llegue un correo, hasta 25 segundos cada vez, así que una espera gasta solo unas pocas llamadas. Eso importa porque el límite es de 120 solicitudes por minuto por cuenta, sumando todas sus claves. Si te pasas, el cliente espera y vuelve a intentarlo.
Enlaces de confirmación y otros correos
Si tu app envía un enlace en vez de un código, usa waitForLink. Sin opciones devuelve el primer enlace del correo; match elige el enlace que contiene un texto o coincide con una RegExp:
const requestedAt = new Date();
await page.getByRole("button", { name: "Enviar enlace" }).click();
const link = await inbox.waitForLink({ match: "/restablecer-contrasena", after: requestedAt });
await page.goto(link);after salta los correos que ya estaban en la bandeja, como el de bienvenida que llegó antes en el mismo test. Para todo lo demás, waitForEmail devuelve el correo completo, con subject, text, html, links, attachments y verificationCode:
const email = await inbox.waitForEmail({ from: "facturacion@", subject: "Tu recibo", after: requestedAt });
expect(email.text).toContain("Total");Cypress
Los tests de Cypress corren en el navegador, así que llama al cliente desde Node con tasks en cypress.config.ts:
import { defineConfig } from "cypress";
import { Saci } from "saci.cc";
const saci = new Saci();
export default defineConfig({
e2e: {
setupNodeEvents(on) {
on("task", {
createInbox: async () => (await saci.createInbox()).address,
waitForCode: (address: string) => saci.inbox(address).waitForCode(),
});
},
},
});En el test, llama a cy.task("waitForCode", address, { timeout: 90_000 }). Sin esa opción, cy.task se rinde a los 60 segundos.
Tus bandejas son privadas
Las bandejas creadas en la web de saci.cc son públicas: cualquiera que sepa la dirección puede leerlas. Las que creas con tu clave son distintas. Durante 7 días pertenecen a tu cuenta: solo las claves de tu cuenta pueden leerlas, y nadie más puede crear una dirección con el mismo nombre. Es lo que quieres en los tests, donde los correos a menudo traen enlaces que inician sesión.
Para una dirección fija, pasa un nombre: createInbox({ name: "prueba-registro" }) crea prueba-registro@saci.cc. Si otra cuenta ya tiene ese nombre, recibes un SaciError con status 409. Los correos se siguen borrando más o menos un día después de llegar, así que no cuentes con ellos de una ejecución a otra.
Webhooks en vez de esperar
Si prefieres que te avisen cuando llega un correo, configura un webhook en la página de tu cuenta. Cada correo que llega a una bandeja de tu cuenta se envía a tu URL en un POST, con la dirección, el remitente, el asunto, los enlaces y el código de verificación, firmado con un secreto para que compruebes que viene de saci.cc. La URL tiene que ser HTTPS y pública, así que localhost no sirve; para tests en tu máquina, esperar con el cliente es más sencillo. La documentación de la API muestra el formato y cómo verificar la firma en Node.
En resumen
- Crea una clave gratis en la página de tu cuenta y ponla en
SACI_API_KEY, en tu máquina y como secret en el CI. - Llama a
createInbox(), regístrate coninbox.addressy luego usawaitForCode()owaitForLink(). - Dale 90 segundos a los tests que esperan un correo.
- Dudas, problemas o un límite más alto: escribe a oi@saci.cc.