import { test, expect } from '@playwright/test'; import { ADMIN_BASE } from '../playwright.config'; /** * Happy-path E2E for MAI CALL v0.1: * operator creates request → admin claims → admin resolves * * Preconditions (met by `pnpm db:seed` + running docker compose): * - Demo Factory tenant with workstations CTR04, QVN_RTL_2, MTG_01 * - admin@demo.local with role ADMIN * - AUTH_DEV_AUTOLOGIN=true on both servers (set in playwright.config.ts) */ test('MAI CALL happy path: create → claim → resolve', async ({ page, context }) => { // Use a unique description so the test can find its own card in the queue. const desc = `E2E ${Date.now()} — ruído anormal no posto`; // ── 0. Badge in to a workstation (the request posto now comes from the // operator's active session, not a per-request dropdown) ──────────── await page.goto('/'); const requestBtn = page.getByTestId('btn-request-maintenance'); if (!(await requestBtn.isVisible().catch(() => false))) { // No active session yet → pick the first workstation in the badge-in panel. await page.getByTestId('badge-in-station').first().click(); await expect(requestBtn).toBeVisible({ timeout: 15_000 }); } // ── 1. Operator creates a request ──────────────────────────────────────── await requestBtn.click(); await page.waitForURL('**/maintenance/new**'); // The workstation is shown read-only from the session; just describe + submit. await page.fill('#description', desc); await expect(page.locator('button[type=submit]')).toBeEnabled({ timeout: 15_000 }); await page.click('button[type=submit]'); // ── 2. Wait for the sync to complete ───────────────────────────────────── // The form queues to IndexedDB; the SyncProvider immediately syncs when // online. The sent page changes from "Em fila" → "Pedido enviado". await page.waitForURL('**/maintenance/sent**'); await expect(page.locator('h1')).toHaveText('Pedido enviado', { timeout: 30_000 }); // ── 3. Admin queue shows the request ───────────────────────────────────── const adminPage = await context.newPage(); await adminPage.goto(`${ADMIN_BASE}/maintenance`); // Find the card that contains our description (admin polls every 5s) const card = adminPage .locator('[data-testid="request-card"]') .filter({ hasText: desc.slice(0, 40) }); await expect(card).toBeVisible({ timeout: 15_000 }); // Card starts as OPEN await expect(card.locator('span', { hasText: 'Aberto' })).toBeVisible(); // ── 4. Admin claims the request ─────────────────────────────────────────── await card.locator('button', { hasText: 'Aceitar' }).click(); // Card changes to CLAIMED await expect(card.locator('span', { hasText: 'Em curso' })).toBeVisible({ timeout: 10_000 }); await expect(card.locator('button', { hasText: 'Aceitar' })).not.toBeVisible(); // ── 5. Enable RESOLVED filter so the card stays visible after resolve ───── await adminPage.locator('label', { hasText: 'Resolvido' }).click(); // ── 6. Admin resolves the request ──────────────────────────────────────── await card.locator('button', { hasText: 'Marcar resolvido' }).click(); const dialog = adminPage.locator('h2', { hasText: 'Marcar como resolvido' }); await expect(dialog).toBeVisible({ timeout: 5_000 }); await adminPage.locator('button', { hasText: 'Confirmar' }).click(); // Card changes to RESOLVED await expect(card.locator('span', { hasText: 'Resolvido' })).toBeVisible({ timeout: 10_000 }); // No action buttons remain on RESOLVED card await expect(card.locator('button', { hasText: 'Aceitar' })).not.toBeVisible(); await expect(card.locator('button', { hasText: 'Marcar resolvido' })).not.toBeVisible(); });