Ajoute 6 scénarios Playwright qui couvrent les surfaces utilisateur
au-delà de l'auth/clients/factures : upload OCR via dropzone, listing
des plans pré-fournis, et la chaîne mailing complète end-to-end.
Import (2 tests) — import.spec.ts :
- Upload PDF via setInputFiles : MockOcrProvider extrait depuis le
filename → drafts créés → SPA redirige sur /factures/import/$batchId
→ on voit le filename listé dans les drafts
- Empty state /factures/import : dropzone + bouton "Parcourir" visibles
Plans (3 tests) — plans.spec.ts :
- Les 4 plans pré-fournis (Standard, Rapide, Patient, Ferme) sont
visibles dès le signup (provisionnés par provisionDefaultPlans)
- Clic "Créer un plan" → arrive sur le wizard /plans/nouveau
- Clic "Modifier" sur une PlanCard → page détail /plans/{slug}
Mailing (1 test, le plus précieux) — mailing.spec.ts :
- Helper helpers/mailpit.ts : clearMailpit, waitForMessageTo (poll
200ms / 10s timeout), getMessage (HTML + texte)
- Scénario : signup → onboarding → créer client avec email →
créer facture saisie manuelle → mark-paid → BullMQ worker enqueue
payment-thanks → email envoyé via SMTP → Mailpit catche →
test inspecte subject + body (numero, montant)
- Valide la chaîne complète : SPA → API → DB → BullMQ → Worker →
Mailpit, en moins de 5 s
Total après cette PR : 26 scénarios Playwright verts en 55 s.
Pré-requis runtime :
- `pnpm dev:up` (Postgres + Redis + Mailpit + MinIO)
- `pnpm e2e:setup` (création DB rubis_test_e2e + migrations)
- Mailpit accessible sur :8025 (clearMailpit l'utilise en beforeEach)
Note check-in flow : pas implémenté en E2E V1 — nécessite un endpoint
test_e2e pour générer un CheckinTask + clear token. Le flow checkin
est déjà couvert par les tests japa functional. À ajouter en PR 3 si
besoin de validation end-to-end UI.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { resetDb } from './helpers/api'
|
|
|
|
/**
|
|
* Scénarios plans de relance.
|
|
*
|
|
* On valide la list view (4 plans pré-fournis après signup) + la
|
|
* navigation vers le wizard de création. Le wizard 4 étapes lui-même
|
|
* (avec génération IA des emails) est testé par les tests japa
|
|
* functional pour le détail métier, et reste hors scope E2E V1.
|
|
*/
|
|
|
|
async function signupAndOnboard(page: import('@playwright/test').Page, tag = 'pl') {
|
|
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 Test')
|
|
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('Plans de relance', () => {
|
|
test.beforeEach(async () => {
|
|
await resetDb()
|
|
})
|
|
|
|
test('les 4 plans pré-fournis sont visibles après signup', async ({ page }) => {
|
|
await signupAndOnboard(page, 'pl-default')
|
|
await page.goto('/plans')
|
|
|
|
await expect(
|
|
page.getByRole('heading', { name: /plans de.*relance/i }),
|
|
).toBeVisible()
|
|
|
|
// Les 4 plans pré-fournis ont des slugs `standard-30j`, `rapide-15j`,
|
|
// `patient-60j`, `ferme-7j`. Leurs noms d'affichage utilisent les mots
|
|
// "Standard", "Rapide", "Patient", "Ferme".
|
|
await expect(page.getByText(/standard/i).first()).toBeVisible()
|
|
await expect(page.getByText(/rapide/i).first()).toBeVisible()
|
|
await expect(page.getByText(/patient/i).first()).toBeVisible()
|
|
await expect(page.getByText(/ferme/i).first()).toBeVisible()
|
|
|
|
// La carte "+ Créer un plan" est aussi visible
|
|
await expect(page.getByRole('link', { name: /créer un plan/i })).toBeVisible()
|
|
})
|
|
|
|
test('clic "Créer un plan" → arrive sur le wizard', async ({ page }) => {
|
|
await signupAndOnboard(page, 'pl-wizard')
|
|
await page.goto('/plans')
|
|
await page.getByRole('link', { name: /créer un plan/i }).click()
|
|
await expect(page).toHaveURL(/\/plans\/nouveau/, { timeout: 5_000 })
|
|
})
|
|
|
|
test('clic "Modifier" sur un plan → page détail du plan', async ({ page }) => {
|
|
await signupAndOnboard(page, 'pl-detail')
|
|
await page.goto('/plans')
|
|
// Chaque PlanCard a un lien "Modifier" → /plans/$slug. On clique sur
|
|
// le premier (qui correspondra à un des 4 plans pré-fournis).
|
|
await page.getByRole('link', { name: /^modifier/i }).first().click()
|
|
await expect(page).toHaveURL(/\/plans\/(standard|rapide|patient|ferme)/, {
|
|
timeout: 5_000,
|
|
})
|
|
})
|
|
})
|