import { test, expect } from '@playwright/test'; import { apiHeaders, login, navigateTo, PORT_SLUG } from '../smoke/helpers'; /** * Smart-archive end-to-end. Spins a throwaway client via API, walks * the wizard via UI, asserts the dossier appeared + the archive ran. * Then walks the smart-restore wizard and asserts the client returns. */ async function createClient(page: import('@playwright/test').Page, name: string) { const headers = await apiHeaders(page); return page.request.post('/api/v1/clients', { headers, data: { fullName: name, contacts: [ { channel: 'email', value: `${name.toLowerCase().replace(/[^a-z0-9]/g, '')}@test.local`, isPrimary: true, }, ], }, failOnStatusCode: false, }); } test.describe('destructive: client smart-archive + smart-restore', () => { test.beforeEach(async ({ page }) => { await login(page, 'super_admin'); }); test('smart-archive single client through the dossier dialog', async ({ page }) => { const name = `SMART-ARCHIVE-${Date.now()}`; const create = await createClient(page, name); if (!create.ok()) { test.skip(true, `client create returned ${create.status()}`); return; } const created = (await create.json()) as { data?: { id?: string } }; const clientId = created.data?.id; if (!clientId) { test.skip(true, 'no clientId in create response'); return; } await page.goto(`/${PORT_SLUG}/clients/${clientId}`); await page.waitForLoadState('networkidle'); const archiveBtn = page.getByRole('button', { name: /archive client/i }).first(); await expect(archiveBtn).toBeVisible({ timeout: 10_000 }); await archiveBtn.click(); // Dossier dialog const dialog = page .getByRole('dialog') .filter({ hasText: /Archive/ }) .first(); await expect(dialog).toBeVisible({ timeout: 10_000 }); // For a freshly-created client there should be no blockers and the // submit button is enabled. const submit = dialog.getByRole('button', { name: /^archive/i }).last(); await expect(submit).toBeEnabled({ timeout: 5_000 }); await submit.click(); // Toast → list refresh → row gone await expect(page.locator('[data-sonner-toast]')).toContainText(name, { timeout: 10_000 }); await navigateTo(page, '/clients'); await expect(page.locator(`tbody tr:has-text("${name}")`).first()).toHaveCount(0); }); test('smart-restore wizard re-enables an archived client', async ({ page }) => { const name = `SMART-RESTORE-${Date.now()}`; const create = await createClient(page, name); if (!create.ok()) { test.skip(true, `client create returned ${create.status()}`); return; } const created = (await create.json()) as { data?: { id?: string } }; const clientId = created.data?.id; if (!clientId) return; // Archive via service-shape API directly to keep this test focused on // the restore wizard. const headers = await apiHeaders(page); const arch = await page.request.post(`/api/v1/clients/${clientId}/archive`, { headers, data: { reason: '', acknowledgedSignedDocuments: false, berthDecisions: [], yachtDecisions: [], reservationDecisions: [], invoiceDecisions: [], documentDecisions: [], }, failOnStatusCode: false, }); if (!arch.ok()) { test.skip(true, `archive call returned ${arch.status()}: ${await arch.text()}`); return; } await page.goto(`/${PORT_SLUG}/clients/${clientId}`); await page.waitForLoadState('networkidle'); // The header should show the Archived badge + a Restore icon button. await expect(page.getByText(/archived/i).first()).toBeVisible({ timeout: 10_000 }); const restoreBtn = page.getByRole('button', { name: /restore client/i }).first(); await expect(restoreBtn).toBeVisible({ timeout: 10_000 }); await restoreBtn.click(); const dialog = page .getByRole('dialog') .filter({ hasText: /Restore/ }) .first(); await expect(dialog).toBeVisible({ timeout: 10_000 }); const submit = dialog.getByRole('button', { name: /^restore client/i }).last(); await expect(submit).toBeEnabled({ timeout: 5_000 }); await submit.click(); await expect(page.locator('[data-sonner-toast]')).toContainText(name, { timeout: 10_000 }); }); });