feat: add memo filter and enhance description filter with regex matching

- Add new memo filter to transaction page (searches :transaction/memo)
- Enhance existing description filter to use case-insensitive regex
- Both filters support wildcard matching via .* pattern
- Add e2e tests for filter functionality
- Update test data with memo fields
This commit is contained in:
2026-05-26 16:34:56 -07:00
parent 712b2c0cb8
commit 200056098f
5 changed files with 367 additions and 4 deletions

View File

@@ -102,6 +102,84 @@ test.describe('Transaction Navigation - Date Filter Persistence', () => {
});
});
async function setTextFilter(page: any, name: string, value: string) {
const input = page.locator(`input[name="${name}"]`).first();
await input.fill(value);
await input.dispatchEvent('change');
await page.waitForTimeout(1000);
}
test.describe('Transaction Filters - Description and Memo', () => {
test('should filter by description with case-insensitive wildcard matching', async ({ page }) => {
await navigateToTransactions(page, '/transaction2');
// Filter by "second" (lowercase) - should match "Second transaction"
await setTextFilter(page, 'description', 'second');
// Wait for URL to update
await page.waitForURL(url => url.search.includes('description=second'), { timeout: 5000 });
// Should show only 1 row (the "Second transaction")
const rowCount = await getTableRowCount(page);
expect(rowCount).toBe(1);
// Verify the row contains "Second transaction"
const rowText = await page.locator('table tbody tr').first().textContent();
expect(rowText).toContain('Second transaction');
});
test('should filter by memo with case-insensitive wildcard matching', async ({ page }) => {
await navigateToTransactions(page, '/transaction2');
// Filter by "rent" (lowercase) - should match "Monthly rent payment"
await setTextFilter(page, 'memo', 'rent');
// Wait for URL to update
await page.waitForURL(url => url.search.includes('memo=rent'), { timeout: 5000 });
// Should show only 1 row (the transaction with "Monthly rent payment" memo)
const rowCount = await getTableRowCount(page);
expect(rowCount).toBe(1);
// Verify the row contains "Test transaction" (the one with the rent memo)
const rowText = await page.locator('table tbody tr').first().textContent();
expect(rowText).toContain('Test transaction');
});
test('should filter by description and memo together', async ({ page }) => {
await navigateToTransactions(page, '/transaction2');
// Set both filters - should match "Test transaction" which has memo "Monthly rent payment"
await setTextFilter(page, 'description', 'test');
await setTextFilter(page, 'memo', 'rent');
// Wait for URL to update
await page.waitForURL(url => url.search.includes('description=test') && url.search.includes('memo=rent'), { timeout: 5000 });
// Should show only 1 row
const rowCount = await getTableRowCount(page);
expect(rowCount).toBe(1);
// Verify it's the "Test transaction" row
const rowText = await page.locator('table tbody tr').first().textContent();
expect(rowText).toContain('Test transaction');
});
test('should show no results when filter does not match', async ({ page }) => {
await navigateToTransactions(page, '/transaction2');
// Filter by something that doesn't exist
await setTextFilter(page, 'description', 'nonexistent');
// Wait for URL to update
await page.waitForURL(url => url.search.includes('description=nonexistent'), { timeout: 5000 });
// Should show no rows
const rowCount = await getTableRowCount(page);
expect(rowCount).toBe(0);
});
});
test.describe('Transaction Sort - Default Newest First', () => {
test('should show transactions sorted by date descending by default', async ({ page }) => {
await navigateToTransactions(page, '/transaction2');