Aller au contenu

Recevoir un e-mail dans un test Playwright

Un parcours d’inscription finit dans une boîte aux lettres, et le test doit lire ce qui y arrive. Cette page appelle l’API directement avec une clé. Aucun plugin Facteur pour Playwright n’est encore publié : il n’y a pas de paquet à installer, et la fonction ci-dessous n’en demande aucun.

  1. Une clé d’API en lecture seule, gardée dans les secrets de votre intégration continue sous le nom FACTEUR_API_KEY. Le guide de démarrage montre comment la créer.
  2. L’identifiant de votre inbox : les huit caractères avant .inbox dans son adresse.

La fonction lance une recherche et la garde ouverte jusqu’à l’arrivée de l’e-mail, vingt secondes au plus :

facteur.ts
const API = 'https://api.facteur.eu'
const INBOX = 'j3k9x2mq'
export async function waitForMail(sentTo: string, wait = 20_000) {
const search = new URLSearchParams({ inbox: INBOX, sentTo, wait: String(wait) })
const response = await fetch(`${API}/messages?${search}`, {
headers: { authorization: `Bearer ${process.env.FACTEUR_API_KEY}` },
})
if (!response.ok) throw new Error(`Facteur answered ${response.status}`)
const { messages } = await response.json()
if (!messages[0]) throw new Error(`Nothing arrived for ${sentTo}`)
return messages[0]
}
import { expect, test } from '@playwright/test'
import { waitForMail } from './facteur'
test('the confirmation code activates the account', async ({ page }) => {
const tag = `run-${Date.now()}`
const address = `signup+${tag}@j3k9x2mq.inbox.facteur.eu`
// Lancée avant le clic : la requête reste ouverte et répond quand l'e-mail arrive.
const arriving = waitForMail(`signup+${tag}`)
// Le parcours d'inscription de votre propre application.
await page.goto('/signup')
await page.getByLabel('Email').fill(address)
await page.getByRole('button', { name: 'Create my account' }).click()
const message = await arriving
expect(message.status).toBe('parsed')
await page.getByLabel('Confirmation code').fill(message.otp)
await page.getByRole('button', { name: 'Confirm' }).click()
await expect(page.getByText('Account verified')).toBeVisible()
})

C’est la seule ligne dont l’ordre compte. La requête part avant le clic, reste ouverte, et répond quand l’e-mail arrive. Lancée après le clic, elle courrait après un message peut-être déjà traité, et vous verriez le défaut le jour où votre intégration continue ralentit.

Toute adresse qui finit par le domaine de votre inbox arrive dedans. Vous n’appelez rien pour créer signup+run-1734@j3k9x2mq.inbox.facteur.eu : vous l’écrivez. C’est ce qui permet à seize tests en parallèle de partager une inbox sans lire le courrier les uns des autres. Une organisation peut garder vingt attentes ouvertes en même temps. Au-delà, la réponse est un 429 avec le code TOO_MANY_WAITS.

Mettez le nom du test et le numéro de tentative dans l’étiquette. Sans le numéro de tentative, un test relancé lit l’e-mail de sa première tentative et réussit à tort, justement quand quelque chose est déjà instable.

message.otp porte le code à usage unique, trouvé à la réception du message. Vous n’écrivez aucune expression régulière, et rien ne casse le jour où le modèle de l’e-mail change. message.links contient les liens dans l’ordre où ils apparaissent, chacun en texte simple : passez-en un à page.goto().

Lisez d’abord message.status. Un message existe un instant avant d’être lu, sans sujet ni code. Une recherche ne renvoie que les messages parsed par défaut : le cas ne se présente que si vous demandez status=any.