import { es } from '@impuestos/i18n'; import { expect, test } from '@playwright/test'; import { readAuditActions, readConsents, readProfile, readVerificationOtp } from './db'; /** * Golden path 1 (SPEC.md section 13): a stranger arrives at the landing page and leaves * with a working account, a consent record and a profile. Every step is the real thing, * including the six digit code, which is read back out of the database because * development prints it to the server console instead of sending it. */ test.describe('onboarding', () => { test('takes a new visitor from the landing page to their first run screen', async ({ page, }, testInfo) => { const email = `nuevo-${testInfo.project.name}-${Date.now()}@demo.local`; const password = 'una-contrasena-larga'; // A1 and A2: the landing hook answers with this taxpayer's real filing day. await page.goto('/es'); await expect(page.getByRole('heading', { name: es['landing.hero.title'] })).toBeVisible(); await page.getByLabel(es['landing.hero.inputLabel']).fill('4123456-1'); await page.getByRole('button', { name: es['landing.hero.cta'] }).click(); // Base ends in 6, so RULES.md puts the deadline on the 19th. await expect(page.getByText('19', { exact: false }).first()).toBeVisible(); await page.getByRole('link', { name: es['landing.preview.cta'] }).click(); // A3: account creation, then the six digit code. await expect(page).toHaveURL(/\/es\/registro$/); await page.getByLabel(es['auth.register.email']).fill(email); await page.getByLabel(es['auth.register.password']).fill(password); await page.getByRole('button', { name: es['auth.register.submit'] }).click(); await expect(page).toHaveURL(/\/es\/verificar/); await expect(page.getByRole('heading', { name: es['auth.otp.title'] })).toBeVisible(); await expect.poll(() => safeOtp(email), { timeout: 10_000 }).toMatch(/^\d{6}$/); const otp = readVerificationOtp(email); // The first box takes the whole pasted code and spreads it across the six. await page.getByLabel('1', { exact: true }).fill(otp); // A4: consent. The primary action stays disabled until data processing is granted. await expect(page).toHaveURL(/\/es\/consentimiento$/); const accept = page.getByRole('button', { name: es['consent.accept'] }); await expect(accept).toBeDisabled(); await page.getByRole('switch', { name: es['consent.dataProcessing'] }).click(); await expect(accept).toBeEnabled(); await accept.click(); // A5: three steps. await expect(page).toHaveURL(/\/es\/configuracion$/); await page.getByLabel(es['setup.fullName']).fill('Nueva Contribuyente'); await page.getByLabel(es['setup.docLabel']).fill('4123456-1'); await page.getByLabel(es['setup.docLabel']).blur(); await expect(page.getByText(es['setup.deadlineNote'].replace('{day}', '19'))).toBeVisible(); await page.getByRole('button', { name: es['common.continue'] }).click(); await page.getByRole('button', { name: es['setup.oblig.iva.title'] }).click(); await page.getByRole('button', { name: es['common.continue'] }).click(); await page.getByRole('button', { name: es['setup.finish'] }).click(); // A6: the guided first run state. await expect(page).toHaveURL(/\/es\/inicio$/); await expect(page.getByRole('heading', { name: es['home.firstRun.title'] })).toBeVisible(); await expect(page.getByRole('link', { name: es['home.firstRun.scan'] })).toBeVisible(); // The flow left the rows behind that it was supposed to. const profile = readProfile(email); expect(profile?.['full_name']).toBe('Nueva Contribuyente'); expect(profile?.['ruc']).toBe('4123456'); expect(profile?.['deadline_digit']).toBe(6); expect(readConsents(email)).toContainEqual({ kind: 'data_processing', revoked: false }); expect(readAuditActions(email)).toContain('consent.grant'); expect(readAuditActions(email)).toContain('profile.create'); }); }); function safeOtp(email: string): string { try { return readVerificationOtp(email); } catch { return ''; } }