otpbox

OTPBox / Guias / Playwright

Playwright

Verificação de e-mail com Playwright

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.

O padrão

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.

Opção 1: REST simples com fetch

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

Opção 2: 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.

Opção 3: fixture 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
});
Status: 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.

Verificando por link em vez de código

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();

Notas para CI

Próximos passos

Pronto para testar no seu próprio app? Crie uma conta gratuita

← Voltar à OTPBox