import { test, expect } from '@playwright/test'; // Acceptance spec for the New/Edit Vendor wizard (info → terms → account → address → legal), // migrated onto the session-backed engine (wizard2). Like New Invoice, the pre-migration // "Next" PUT /admin/vendor/navigat 500d on the empty route-params {}→nil quirk (a // [:map [:db/id …]] route-schema on a route with no path param), so this is an ACCEPTANCE // gate: green on the engine. Required fields: vendor/name (min 3) on info, vendor/default-account // on account. test.beforeEach(async ({ request }) => { await request.post('/test-reset'); }); async function seedAccount(page: any): Promise { const info = await (await page.request.get('/test-info')).json(); return info.accounts['test-account']; } async function openNewVendor(page: any) { await page.goto('/admin/vendor'); await page.waitForSelector('#entity-table'); await page.locator('button:has-text("New Vendor")').first().click(); await page.waitForSelector('#wizard-form'); await page.waitForTimeout(400); } // The advance/save button is the engine's data-primary nav button. const primary = (page: any) => page.locator('#wizard-form button[data-primary]').first().click(); async function setHidden(page: any, name: string, value: number) { await page.evaluate(({ name, value }: { name: string; value: number }) => { const h = document.querySelector(`input[name="${name}"]`) as HTMLInputElement; h.value = String(value); h.dispatchEvent(new Event('change', { bubbles: true })); }, { name, value }); } test.describe.configure({ mode: 'serial' }); test.describe('Vendor wizard (acceptance)', () => { test('info step renders with the name field and a timeline', async ({ page }) => { await openNewVendor(page); const form = page.locator('#wizard-form'); await expect(form).toContainText('Basic Info'); await expect(form).toContainText('Terms'); // timeline step await expect(form.locator('input[name="vendor/name"]')).toBeVisible(); }); test('create a vendor across all 5 steps adds it to the grid', async ({ page }) => { const account = await seedAccount(page); await openNewVendor(page); // info await page.locator('input[name="vendor/name"]').fill('Acme Supplies'); await primary(page); // -> terms await page.waitForTimeout(500); await expect(page.locator('#wizard-form')).toContainText('Terms Overrides'); await primary(page); // -> account await page.waitForTimeout(500); await expect(page.locator('#wizard-form')).toContainText('Default Account'); await setHidden(page, 'vendor/default-account', account); await primary(page); // -> address await page.waitForTimeout(500); await expect(page.locator('#wizard-form')).toContainText('Street'); await primary(page); // -> legal await page.waitForTimeout(500); await expect(page.locator('#wizard-form')).toContainText('Legal Entity'); await primary(page); // Save await page.waitForTimeout(1200); // the vendor persists: reload the grid and it's there await page.goto('/admin/vendor'); await page.waitForSelector('#entity-table tbody tr'); await expect(page.locator('#entity-table')).toContainText('Acme Supplies'); }); test('edit opens prefilled and a rename persists', async ({ page }) => { const account = await seedAccount(page); await page.goto('/admin/vendor'); await page.waitForSelector('#entity-table tbody tr'); // open the edit wizard for the seeded "Test Vendor" (its row pencil) await page.locator('#entity-table tbody tr', { hasText: 'Test Vendor' }).first() .locator('[hx-get*="/edit"]').first().click(); await page.waitForSelector('#wizard-form'); await page.waitForTimeout(400); // info step is prefilled with the existing name await expect(page.locator('input[name="vendor/name"]')).toHaveValue('Test Vendor'); await page.locator('input[name="vendor/name"]').fill('Test Vendor RENAMED'); await primary(page); await page.waitForTimeout(400); // terms await primary(page); await page.waitForTimeout(400); // account (default-account already set) await setHidden(page, 'vendor/default-account', account); await primary(page); await page.waitForTimeout(400); // address await primary(page); await page.waitForTimeout(400); // legal await primary(page); await page.waitForTimeout(1000); // save await page.goto('/admin/vendor'); await page.waitForSelector('#entity-table tbody tr'); await expect(page.locator('#entity-table')).toContainText('Test Vendor RENAMED'); }); test('info step blocks advancing when the name is too short', async ({ page }) => { await openNewVendor(page); await page.locator('input[name="vendor/name"]').fill('ab'); // < 3 chars await primary(page); await page.waitForTimeout(500); // still on the info step (validation re-renders it, no advance) await expect(page.locator('#wizard-form')).toContainText('Basic Info'); }); });