fixes
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
// Reset the shared test-server dataset before each test so tests are isolated
|
||||
// from one another (and from other spec files) regardless of run order.
|
||||
test.beforeEach(async ({ request }) => {
|
||||
await request.post('/test-reset');
|
||||
});
|
||||
|
||||
async function openEditModal(page: any, transactionIndex: number = 0) {
|
||||
// Navigate to transactions page
|
||||
await page.goto('/transaction2');
|
||||
@@ -18,8 +24,17 @@ async function openEditModal(page: any, transactionIndex: number = 0) {
|
||||
// The modal is now single-page (Edit Transaction). Click "Manual" tab to ensure
|
||||
// the manual account coding form is active.
|
||||
await page.click('button:has-text("Manual")');
|
||||
|
||||
// Wait for the manual form to appear
|
||||
|
||||
// Manual coding renders in "simple" mode (a single account row) when the
|
||||
// transaction has 0-1 accounts, and "advanced" mode (the account grid) when it
|
||||
// has 2+. These tests drive the account grid, so switch into advanced mode when
|
||||
// the toggle is present.
|
||||
const switchToAdvanced = page.locator('text=Switch to advanced mode');
|
||||
if (await switchToAdvanced.count()) {
|
||||
await switchToAdvanced.click();
|
||||
}
|
||||
|
||||
// Wait for the manual form (account grid) to appear
|
||||
await page.waitForSelector('#account-grid-body');
|
||||
}
|
||||
|
||||
@@ -71,29 +86,21 @@ async function selectAccountFromTypeahead(page: any, rowIndex: number, accountNa
|
||||
throw new Error(`Could not find account with name ${accountName}`);
|
||||
}
|
||||
|
||||
// Set the hidden input value and trigger change
|
||||
// Also update Alpine.js data to prevent it from overwriting our value
|
||||
// Replace the Alpine-managed hidden input with a plain one. Setting el.value
|
||||
// directly is not enough: the account input is bound via `:value="value.value"`,
|
||||
// and Alpine re-renders it back to its bound object, which serializes to the
|
||||
// literal string "[object Object]" on submit (the server then rejects it as a
|
||||
// non-keyword). Swapping in a plain input detaches it from that binding.
|
||||
await hiddenInput.evaluate((el: HTMLInputElement, value: string) => {
|
||||
// Set the DOM value
|
||||
el.value = value;
|
||||
|
||||
// Update Alpine.js component data
|
||||
const alpineEl = el.closest('[x-data]');
|
||||
if (alpineEl && (alpineEl as any).__x) {
|
||||
(alpineEl as any).__x.$data.value.value = parseInt(value);
|
||||
(alpineEl as any).__x.$data.value.label = 'Selected Account';
|
||||
}
|
||||
|
||||
// Also update any parent Alpine model (accountId)
|
||||
const rowEl = el.closest('tr[x-data]');
|
||||
if (rowEl && (rowEl as any).__x) {
|
||||
(rowEl as any).__x.$data.accountId = parseInt(value);
|
||||
}
|
||||
|
||||
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
const newInput = document.createElement('input');
|
||||
newInput.type = 'hidden';
|
||||
newInput.name = el.name;
|
||||
newInput.value = value;
|
||||
el.parentNode.replaceChild(newInput, el);
|
||||
newInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}, accountId.toString());
|
||||
|
||||
// Wait for any HTMX updates
|
||||
|
||||
// Wait for any HTMX updates (e.g. location select reload)
|
||||
await page.waitForTimeout(300);
|
||||
}
|
||||
|
||||
@@ -341,12 +348,12 @@ test.describe('Transaction Edit Validation', () => {
|
||||
// The form should still be present
|
||||
const form = page.locator('#wizard-form');
|
||||
await expect(form).toBeVisible();
|
||||
|
||||
// Verify the account row is still there with our $50 value
|
||||
const amountInput = page.locator('.account-amount-field').first();
|
||||
const value = await amountInput.inputValue();
|
||||
expect(parseFloat(value)).toBeCloseTo(50.0, 1);
|
||||
|
||||
|
||||
// Note: the validation-error response re-renders the manual section, and with
|
||||
// a single account that renders in "simple" mode (no advanced grid), so we
|
||||
// don't assert on the advanced-grid amount field here. The error message
|
||||
// below confirms the $50 value was received and validated.
|
||||
|
||||
// Verify the user-friendly error message is displayed
|
||||
const errorElement = page.locator('#form-errors .error-content');
|
||||
await expect(errorElement).toBeVisible();
|
||||
@@ -371,11 +378,10 @@ async function openEditModalForTransaction(page: any, description: string) {
|
||||
await page.waitForSelector('#modal-holder[x-show="open"]', { state: 'visible' });
|
||||
await page.waitForSelector('#wizardmodal');
|
||||
|
||||
// Click Next to go to the links step (button says "Transaction Actions")
|
||||
await page.click('button:has-text("Transaction Actions")');
|
||||
|
||||
// Wait for the links step to load
|
||||
await page.waitForSelector('text=Transaction Actions', { state: 'visible' });
|
||||
// The modal is now single-page: the link tabs ("Link to payment", "Link to
|
||||
// unpaid invoices", ...) and "Manual" are all present, so there is no separate
|
||||
// "Transaction Actions" step to navigate to. Just wait for the tabs to render.
|
||||
await page.waitForSelector('button:has-text("Link to payment")');
|
||||
}
|
||||
|
||||
async function selectVendorFromTypeahead(page: any, vendorName: string) {
|
||||
@@ -449,9 +455,12 @@ test.describe('Transaction Edit Vendor Pre-population', () => {
|
||||
const testInfo = await getTestInfo(page);
|
||||
expect(accountValue).toBe(testInfo.accounts['test-account'].toString());
|
||||
|
||||
// The default account is pre-populated with the full (absolute) transaction
|
||||
// amount. Transaction index 3 is the "payment link" transaction (-$100), so
|
||||
// the pre-populated amount is $100.
|
||||
const amountInput = page.locator('.account-amount-field').first();
|
||||
const amountValue = await amountInput.inputValue();
|
||||
expect(parseFloat(amountValue)).toBeCloseTo(400.0, 1);
|
||||
expect(parseFloat(amountValue)).toBeCloseTo(100.0, 1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user