import { test, expect } from '@playwright/test'; // Characterization spec for the POS Sales Summary edit modal. Captures CURRENT // (pre-migration) behavior so the wizard -> plain-Selmer migration can be proven // behavior-preserving. Reset the shared dataset before each test for isolation. test.beforeEach(async ({ request }) => { await request.post('/test-reset'); }); async function getTestInfo(page: any) { return (await page.request.get('/test-info')).json(); } async function openEditModal(page: any) { await page.setExtraHTTPHeaders({ 'x-clients': '"mine"' }); await page.goto('/pos/summaries'); await page.waitForSelector('#entity-table tbody tr'); // The row's edit button is an hx-get to /pos/summaries/ (the edit-wizard route). await page.locator('#entity-table tbody tr').first() .locator('a[hx-get], button[hx-get]').first().click(); await page.waitForSelector('#wizardmodal'); } test.describe.configure({ mode: 'serial' }); test.describe('Sales Summary Edit (characterization)', () => { test('opens the edit modal with debit/credit columns, categories, accounts and amounts', async ({ page }) => { await openEditModal(page); const modal = page.locator('#wizardmodal'); await expect(modal).toContainText('Edit Summary'); await expect(modal).toContainText('Debits'); await expect(modal).toContainText('Credits'); // seeded items await expect(modal).toContainText('Cash Deposit'); // debit item category await expect(modal).toContainText('Food Sales'); // credit item category // resolved account names (account-display-cell pulls the account name) await expect(modal).toContainText('Second Account'); // debit item account await expect(modal).toContainText('Test Account'); // credit item account // amounts render await expect(modal).toContainText('$500.00'); // two account cells, each with an inline-edit pencil expect(await modal.locator('.account-cell').count()).toBe(2); expect(await modal.locator('[hx-get*="edit/item-account"]').count()).toBe(2); }); test('seeded summary is balanced (no out-of-balance indicator)', async ({ page }) => { await openEditModal(page); const modal = page.locator('#wizardmodal'); // balanced: $500 debit == $500 credit, so no unbalanced warning text await expect(modal).not.toContainText('Out of balance'); await expect(modal).not.toContainText('Unbalanced'); }); test('inline account edit: pencil opens the typeahead editor; cancel restores the display', async ({ page }) => { await openEditModal(page); const modal = page.locator('#wizardmodal'); // The debit row shows "Second Account" with a pencil. Click it -> account-edit-cell. const debitCell = modal.locator('.account-cell', { hasText: 'Second Account' }).first(); await debitCell.locator('[hx-get*="edit/item-account"]').click(); // edit cell: a typeahead plus check (save) + cancel buttons const editCell = modal.locator('.account-cell').filter({ has: page.locator('[hx-put*="save-item-account"]') }).first(); await expect(editCell.locator('[hx-put*="save-item-account"]')).toBeVisible(); await expect(editCell.locator('[hx-get*="cancel-item-account"]')).toBeVisible(); // Cancel -> back to display mode showing the original account await editCell.locator('[hx-get*="cancel-item-account"]').click(); await page.waitForTimeout(300); await expect(modal.locator('.account-cell', { hasText: 'Second Account' }).first()).toBeVisible(); // back in display mode: the pencil (edit) is shown again await expect(modal.locator('.account-cell', { hasText: 'Second Account' }) .first().locator('[hx-get*="edit/item-account"]')).toBeVisible(); }); test('inline account edit: save (check) re-renders the account display cell', async ({ page }) => { await openEditModal(page); const modal = page.locator('#wizardmodal'); const creditCell = modal.locator('.account-cell', { hasText: 'Test Account' }).first(); await creditCell.locator('[hx-get*="edit/item-account"]').click(); const editCell = modal.locator('.account-cell').filter({ has: page.locator('[hx-put*="save-item-account"]') }).first(); await expect(editCell.locator('[hx-put*="save-item-account"]')).toBeVisible(); // Save without changing -> display cell re-renders, account preserved, pencil back. await editCell.locator('[hx-put*="save-item-account"]').click(); await page.waitForTimeout(300); const display = modal.locator('.account-cell', { hasText: 'Test Account' }).first(); await expect(display).toBeVisible(); await expect(display.locator('[hx-get*="edit/item-account"]')).toBeVisible(); }); // NOTE: the "New Summary Item" button is currently BROKEN in this modal and is // therefore intentionally not characterized as working. Its Alpine handler // (`@click="$dispatch('newRow', {index: (newRowIndex++)})"`) throws // "newRowIndex is not defined" (the modal's x-data is empty), and even if it // fired, `hx-target="closest .new-row"` matches no ancestor in this div layout, // so the `new-summary-item` route never fires. We assert the *current* behavior: // the button is present but adds nothing. The migration may fix or preserve this. test('New Summary Item button is present but currently inert (documents the break)', async ({ page }) => { await openEditModal(page); const modal = page.locator('#wizardmodal'); const before = await modal.locator('input').count(); await modal.locator('[hx-get*="sales-summary-item"]').first().click(); await page.waitForTimeout(500); // no manual row was added (no category input appeared, input count unchanged) expect(await modal.locator('input[placeholder="Category/Explanation"]').count()).toBe(0); expect(await modal.locator('input').count()).toBe(before); }); test('Save closes the modal and the summary stays in the grid', async ({ page }) => { await openEditModal(page); const modal = page.locator('#wizardmodal'); // submit the form via the Save button; the PUT swaps the grid row + fires modalclose const putResp = page.waitForResponse(r => r.url().includes('/pos/summaries') && r.request().method() === 'PUT'); await modal.locator('button[type="submit"]').click(); expect((await putResp).status()).toBe(200); // modalclose hides the modal (it is hidden, not removed from the DOM) await expect(modal).toBeHidden({ timeout: 5000 }); // the grid still shows the summary row await expect(page.locator('#entity-table tbody tr')).toHaveCount(1); }); });