import { test, expect } from '@playwright/test' import { resetDb } from './helpers/api' import { clearMailpit, getMessage, waitForMessageTo } from './helpers/mailpit' /** * Scénarios mailing — la chaîne complète passe par Mailpit (SMTP local * sur :1025, UI/API :8025). * * Le scénario validé ici : créer une facture, la marquer encaissée → * BullMQ worker enqueue le job `payment-thanks` → email "Merci, paiement * bien reçu" envoyé au client final → Mailpit catche → on inspecte * subject + body via l'API Mailpit. * * Pré-requis : * - Mailpit container up (`pnpm dev:up`) * - Redis container up (BullMQ workers démarrent au boot API) * * Latence typique : ~2 s entre le clic mark-paid et Mailpit (job BullMQ * + render React Email + envoi SMTP). On poll Mailpit jusqu'à 10 s. */ async function signupAndOnboard( page: import('@playwright/test').Page, tag = 'mail', ) { const email = `${tag}+${Date.now()}@rubis.test` await page.goto('/signup') await page.getByLabel(/Prénom \/ Nom/i).fill('Test User') await page.getByLabel(/Email professionnel/i).fill(email) await page.getByLabel(/Mot de passe/i).fill('motdepasse-fort-123') await page.getByRole('button', { name: /créer mon compte/i }).click() await page.waitForURL(/\/onboarding\/compte/, { timeout: 10_000 }) await page.getByRole('button', { name: /continuer/i }).click() await page.waitForURL(/\/onboarding\/entreprise/) await page.getByLabel(/nom de l'entreprise/i).fill('Atelier Mail') await page.getByRole('button', { name: /continuer/i }).click() await page.waitForURL(/\/onboarding\/signature/) await page.getByRole('button', { name: /terminer/i }).click() await page.waitForURL('/', { timeout: 10_000 }) } test.describe('Mailing — payment thanks via Mailpit', () => { test.beforeEach(async () => { await resetDb() await clearMailpit() }) test('mark-paid déclenche l\'email "Merci, paiement bien reçu" au client', async ({ page, }) => { const clientEmail = `client-mail-${Date.now()}@example.test` await signupAndOnboard(page, 'mail-thanks') // 1. Créer un client avec l'email qu'on va surveiller dans Mailpit await page.goto('/clients') await page.getByRole('button', { name: /nouveau client/i }).first().click() await page.getByLabel(/^Nom du client$/i).fill('Boulangerie Mail Test') await page.getByLabel(/^Email du contact$/i).fill(clientEmail) await page.getByRole('button', { name: /^créer le client$/i }).click() await expect(page.getByText('Boulangerie Mail Test').first()).toBeVisible({ timeout: 5_000, }) // 2. Créer une facture saisie manuelle await page.getByRole('button', { name: /^saisir$/i }).first().click() await expect(page.getByRole('dialog')).toBeVisible() const combobox = page.getByPlaceholder(/rechercher.*créer un client/i) await combobox.fill('Boulangerie Mail') await expect( page.getByRole('option', { name: /Boulangerie Mail Test/i }), ).toBeVisible({ timeout: 5_000 }) await page.getByRole('option', { name: /Boulangerie Mail Test/i }).click() await page.getByLabel(/N° de facture/i).fill('F-MAIL-001') await page.getByLabel(/Montant TTC/i).fill('1240') await page.getByLabel(/Plan de relance/i).click() await page.getByRole('option').first().click() await page.getByRole('button', { name: /^créer la facture$/i }).click() // 3. Aller sur la liste, ouvrir le détail de la facture await page.goto('/factures') await expect(page.getByText('F-MAIL-001').first()).toBeVisible({ timeout: 5_000 }) await page.getByText('F-MAIL-001').first().click() // 4. Cliquer "Marquer encaissée" await page.getByRole('button', { name: /marquer encaissée/i }).click() // Toast de confirmation await expect(page.getByText(/encaissée.*rubis/i)).toBeVisible({ timeout: 5_000 }) // 5. Attendre l'email Mailpit au client const msg = await waitForMessageTo(clientEmail, { subjectIncludes: 'F-MAIL-001', timeoutMs: 15_000, }) expect(msg.From.Address).toBeTruthy() // 6. Vérifier le contenu : le body mentionne le numero + montant const detail = await getMessage(msg.ID) expect(detail.Subject).toMatch(/paiement|reçu|F-MAIL-001/i) expect(detail.Text + detail.HTML).toMatch(/F-MAIL-001/) expect(detail.Text + detail.HTML).toMatch(/1\s*240/) // "1 240 €" ou variation }) })