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.
Avant de commencer
Section intitulée « Avant de commencer »- 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. - L’identifiant de votre inbox : les huit caractères avant
.inboxdans son adresse.
La fonction
Section intitulée « La fonction »La fonction lance une recherche et la garde ouverte jusqu’à l’arrivée de l’e-mail, vingt secondes au plus :
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()})Lancer l’attente avant l’action
Section intitulée « Lancer l’attente avant l’action »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.
Une adresse par test, sans rien créer
Section intitulée « Une adresse par test, sans rien créer »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.
Le code est déjà sorti du message
Section intitulée « Le code est déjà sorti du message »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.