Um fluxo de cadastro que termina em "confira seu e-mail para ver o código" é difícil de testar de ponta a ponta, porque o teste precisa de uma caixa de entrada que realmente exista e realmente receba e-mails. O OTPBox dá ao Playwright um endereço descartável real e uma forma de aguardar o código que ele recebe — sem simular o provedor de e-mail, sem uma conta de teste compartilhada em que outras execuções colidem.
Cada abordagem abaixo segue os mesmos quatro passos: criar uma caixa real, usar o endereço dela no formulário de cadastro, bloquear até que o OTP ou o link de verificação chegue, e então concluir o fluxo e limpar. A diferença está apenas em quanto código repetitivo você mesmo escreve.
fetchSem nenhuma dependência além do Playwright. Gere uma chave gratuita uma vez (200 requisições/mês, ou use uma chave de organização para CI — veja Obter uma chave), e então chame a API diretamente dentro do teste:
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 é o cliente oficial em TypeScript, publicado no npm, e elimina o loop manual de polling com 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() faz polling em getMessages() até que uma mensagem recebida depois do início da chamada tenha um código extraído, ou até o tempo esgotar (retorna null em caso de timeout — code! acima é aceitável depois de você validar isso no seu próprio teste, mas verifique null em código real). Também há waitForEmail() caso você queira a mensagem inteira, e getUsage() caso uma suíte queira confirmar que não estourou sua cota.
otpbox-playwright (em desenvolvimento)Um pacote dedicado para Playwright, otpbox-playwright, envolve o otpbox-sdk em uma fixture de test personalizada, para que uma spec nem precise chamar createInbox()/deleteInbox() diretamente:
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 ainda não foi publicado no npm. É código real e funcional no mesmo monorepo do otpbox-sdk, mas até ser lançado como pacote você o instalaria com npm link a partir de um clone do repositório, em vez de npm install otpbox-playwright. Até lá, a Opção 2 (otpbox-sdk diretamente) é o caminho suportado e oferece o mesmo comportamento de waitForOtp()/waitForVerificationLink() com algumas linhas a mais de configuração.Alguns fluxos de cadastro enviam um link de confirmação em vez de um código de 6 dígitos. A mesma caixa funciona — use waitForEmail() e leia message.link (ou, com otpbox-sdk, busque a mensagem completa com getMessage(messageId) para acessar 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 um secret do repositório, nunca no YAML do workflow — veja a seção CI/CD da documentação para exemplos com GitHub Actions, GitLab CI, Jenkins e CircleCI.Idempotency-Key em POST /api/v1/inboxes se o seu CI tentar novamente chamadas de rede instáveis, para que uma nova tentativa não crie uma segunda caixa — veja Chaves de idempotência.finally para que um teste que falhe não a deixe órfã — embora o cron de expiração de 15 minutos limpe de qualquer forma.Pronto para testar no seu próprio app? Crie uma conta gratuita
← Voltar à OTPBox