import { es } from '@impuestos/i18n'; import { expect, test, type Page } from '@playwright/test'; async function signIn(page: Page): Promise { await page.goto('/es/login'); await page.getByLabel(es['auth.register.email']).fill('maria@demo.local'); await page.getByLabel(es['auth.login.password']).fill('demo-maria-1'); await page.getByRole('button', { name: es['auth.login.submit'] }).click(); await expect(page).toHaveURL(/\/es\/inicio$/); } /** Flow C, read only: the position, the single next action and the insight feed. */ test.describe('dashboard', () => { test.beforeEach(async ({ page }) => { await signIn(page); }); test('shows the IVA and IRP position with real figures', async ({ page }) => { await expect(page.getByRole('heading', { name: es['home.title'] })).toBeVisible(); // Both obligations are active for the showcase account. await expect(page.getByText(es['home.iva.title'].split('{')[0] as string)).toBeVisible(); await expect(page.getByText(es['home.irp.title'].split('{')[0] as string)).toBeVisible(); // Money is always Gs. and never localized. await expect(page.getByText(/Gs\. [\d.]+/).first()).toBeVisible(); }); test('opens the documents behind a headline number', async ({ page }) => { await expect(page.getByText(es['home.iva.title'].split('{')[0] as string)).toBeVisible(); await page.getByRole('button', { name: /^Gs\./ }).first().click(); const dialog = page.getByRole('dialog'); await expect(dialog).toBeVisible(); await expect(dialog.getByRole('heading', { name: es['home.trace.title'] })).toBeVisible(); await dialog.getByRole('button', { name: es['common.close'] }).click(); await expect(dialog).toBeHidden(); }); test('offers exactly one next action', async ({ page }) => { // Wait for the position, or the action strip has not rendered yet. await expect(page.getByText(es['home.iva.title'].split('{')[0] as string)).toBeVisible(); // Whichever card wins, there is one of them and it has one button. const shell = page.locator('main'); const actions = shell.getByRole('link', { name: new RegExp( [ es['home.next.overdueCta'], es['home.next.reviewCta'], es['home.next.deadlineCta'], es['home.next.bandejaCta'], ].join('|'), ), }); // all_clear is the one kind with nothing to click, which is still one card. const allClear = page.getByText(es['home.next.allClear']); expect((await actions.count()) + (await allClear.count())).toBe(1); }); test('dismisses an insight and does not offer it again', async ({ page }) => { // Wait for the position to land, or the feed has not rendered yet. await expect(page.getByText(es['home.iva.title'].split('{')[0] as string)).toBeVisible(); const dismiss = page.getByRole('button', { name: es['home.insight.dismiss'] }); const before = await dismiss.count(); // Dismissals persist by design, so a stack that has already been cleared by an earlier // run has nothing to offer. The persistence guarantee itself is covered against the // API in dashboard.test.ts, which starts from a fresh database every time. test.skip(before === 0, 'every insight for this account has already been dismissed'); await dismiss.first().click(); await expect(async () => { expect(await page.getByRole('button', { name: es['home.insight.dismiss'] }).count()).toBeLessThan(before); }).toPass({ timeout: 10_000 }); await page.reload(); expect(await page.getByRole('button', { name: es['home.insight.dismiss'] }).count()).toBeLessThan(before); }); }); test.describe('the app shell', () => { test.beforeEach(async ({ page }) => { await signIn(page); }); test('carries the scan button and the four tabs on every screen', async ({ page }) => { for (const path of ['/es/inicio', '/es/bandeja', '/es/comprobantes', '/es/vencimientos']) { await page.goto(path); await expect(page.getByRole('link', { name: es['scan.fab'] })).toBeVisible(); for (const key of ['nav.inicio', 'nav.bandeja', 'nav.comprobantes', 'nav.vencimientos'] as const) { await expect(page.getByRole('link', { name: es[key], exact: true })).toBeVisible(); } } }); test('the scan button reaches the scanner', async ({ page }) => { await page.getByRole('link', { name: es['scan.fab'] }).click(); await expect(page).toHaveURL(/\/es\/escanear$/); }); }); test.describe('vencimientos', () => { test('explains the filing day and lists what is coming', async ({ page }) => { await signIn(page); await page.goto('/es/vencimientos'); await expect(page.getByRole('heading', { name: es['vto.title'] })).toBeVisible(); // Her RUC base ends in 6, which RULES.md puts on the 19th. await expect( page.getByText(es['vto.explainer'].replace('{digit}', '6').replace('{day}', '19')), ).toBeVisible(); await expect(page.getByRole('listitem').first()).toBeVisible(); await expect(page.getByText(es['obligation.iva_120']).first()).toBeVisible(); }); });