import { expect, test } from 'playwright/test'; /** * Plantilla de pruebas para el contrato de estados. * * Requisitos de la app bajo prueba: * - STATE_ROUTE apunta a una vista que acepta `?scenario=`. * - la región principal tiene nombre accesible "Pedidos"; * - los datos se representan como tabla; * - los controles internos sólo existen en entorno de test. * * Ejecución: * STATE_ROUTE=http://localhost:4321/pedidos \ * npx playwright test ui-states.spec.ts */ const route = process.env.STATE_ROUTE ?? 'http://localhost:4321/pedidos'; const scenarioUrl = (scenario: string) => `${route}${route.includes('?') ? '&' : '?'}scenario=${scenario}`; test.describe('contrato de estados de Pedidos', () => { test('loading no afirma que el resultado está vacío', async ({ page }) => { await page.goto(scenarioUrl('loading')); const region = page.getByRole('region', { name: 'Pedidos' }); await expect(region).toHaveAttribute('aria-busy', 'true'); await expect(page.getByText(/cargando pedidos/i)).toBeVisible(); await expect(page.getByText(/no hay pedidos/i)).toHaveCount(0); }); test('refreshing conserva datos y comunica finalización', async ({ page }) => { await page.goto(scenarioUrl('refreshing')); const region = page.getByRole('region', { name: 'Pedidos' }); await expect(region).toHaveAttribute('aria-busy', 'true'); await expect(page.getByRole('table')).toBeVisible(); await expect(page.getByText(/actualizando pedidos/i)).toBeVisible(); const trigger = page.getByTestId('resolve-refresh'); await expect(trigger).toBeAttached(); await trigger.click(); await expect(region).toHaveAttribute('aria-busy', 'false'); await expect(page.getByRole('status')).toContainText(/actualizados/i); await expect(page.getByRole('table')).toBeVisible(); }); test('empty de primera vez ofrece una acción concreta', async ({ page }) => { await page.goto(scenarioUrl('empty-first-use')); await expect( page.getByRole('heading', { name: /todavía no hay pedidos/i }), ).toBeVisible(); await expect( page.getByRole('button', { name: /crear pedido/i }), ).toBeEnabled(); }); test('empty por filtro conserva contexto y permite limpiar', async ({ page }) => { await page.goto(scenarioUrl('empty-filter')); await expect(page.getByText(/sin coincidencias/i)).toBeVisible(); await expect(page.getByText(/acme/i)).toBeVisible(); await expect( page.getByRole('button', { name: /limpiar/i }), ).toBeEnabled(); }); test('error recuperable conserva datos y reintenta', async ({ page }) => { await page.goto(scenarioUrl('error-retryable')); await expect(page.getByText(/no pudimos actualizar/i)).toBeVisible(); await expect(page.getByText(/sigues viendo/i)).toBeVisible(); await expect(page.getByRole('table')).toBeVisible(); await expect( page.getByRole('button', { name: /reintentar/i }), ).toBeEnabled(); }); test('forbidden explica el permiso y no ofrece reintento ciego', async ({ page }) => { await page.goto(scenarioUrl('forbidden')); await expect(page.getByText(/no tienes permiso/i)).toBeVisible(); await expect( page.getByRole('button', { name: /reintentar/i }), ).toHaveCount(0); }); test('progreso expone nombre, rango y valor', async ({ page }) => { await page.goto(scenarioUrl('progress')); const progress = page.getByRole('progressbar', { name: /importando clientes/i, }); await expect(progress).toHaveAttribute('aria-valuemin', '0'); await expect(progress).toHaveAttribute('aria-valuemax', '1000'); await expect(progress).toHaveAttribute('aria-valuenow', '670'); }); test('status dinámico no secuestra el foco', async ({ page }) => { await page.goto(scenarioUrl('ready')); const refresh = page.getByRole('button', { name: /actualizar pedidos/i }); await refresh.focus(); await refresh.click(); await expect(refresh).toBeFocused(); await expect(page.getByRole('status')).toContainText( /actualizando|actualizados/i, ); }); });