import { test, expect } from '@playwright/test'; // Mock data removed - was unused const mockLedgerEntries = [ { id: 'ledger-1', orgId: 'test-org-1', eventId: 'test-event-1', orderId: 'test-order-1', type: 'sale', amountCents: 15000, currency: 'USD', createdAt: new Date().toISOString(), stripe: { balanceTxnId: 'txn_mock_1', chargeId: 'ch_mock_1', accountId: 'acct_mock_1', }, }, { id: 'ledger-2', orgId: 'test-org-1', eventId: 'test-event-1', orderId: 'test-order-1', type: 'platform_fee', amountCents: 450, currency: 'USD', createdAt: new Date().toISOString(), stripe: { balanceTxnId: 'txn_mock_1', chargeId: 'ch_mock_1', accountId: 'acct_mock_1', }, }, { id: 'ledger-3', orgId: 'test-org-1', eventId: 'test-event-1', orderId: 'test-order-1', type: 'fee', amountCents: -465, currency: 'USD', createdAt: new Date().toISOString(), stripe: { balanceTxnId: 'txn_mock_1', chargeId: 'ch_mock_1', accountId: 'acct_mock_1', }, }, ]; test.describe('Refunds and Disputes System', () => { test.beforeEach(async ({ page }) => { // Mock network requests for refunds API await page.route('**/createRefund', async (route) => { const request = await route.request().postDataJSON(); // Simulate validation errors if (!request.orderId) { await route.fulfill({ status: 400, contentType: 'application/json', body: JSON.stringify({ error: 'orderId is required' }) }); return; } if (request.amountCents && request.amountCents > 15000) { await route.fulfill({ status: 400, contentType: 'application/json', body: JSON.stringify({ error: 'Invalid refund amount: exceeds order total', details: `Refund amount ${request.amountCents} exceeds order total 15000` }) }); return; } // Simulate successful refund await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ refundId: 'refund_mock_123', stripeRefundId: 're_mock_123', amountCents: request.amountCents || 15000, status: 'succeeded' }) }); }); // Mock get order refunds API await page.route('**/getOrderRefunds', async (route) => { const request = await route.request().postDataJSON(); if (request.orderId === 'test-order-1') { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ refunds: [ { id: 'refund_mock_123', amountCents: 7500, status: 'succeeded', createdAt: new Date().toISOString(), reason: 'Customer request' } ] }) }); } else { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ refunds: [] }) }); } }); // Mock reconciliation API await page.route('**/getReconciliationData', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ summary: { grossSales: 15000, refunds: 0, stripeFees: 465, platformFees: 450, disputeFees: 0, netToOrganizer: 14085, totalTransactions: 1, period: { start: '2024-01-01', end: '2024-12-31' } }, entries: mockLedgerEntries, total: mockLedgerEntries.length }) }); }); // Mock events API for reconciliation await page.route('**/getReconciliationEvents', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ events: [ { id: 'test-event-1', name: 'Test Concert 2024', startAt: '2024-08-01T19:00:00Z' } ] }) }); }); // Mock dispute API await page.route('**/getOrderDisputes', async (route) => { const request = await route.request().postDataJSON(); if (request.orderId === 'disputed-order') { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ orderId: 'disputed-order', dispute: { disputeId: 'dp_mock_789', status: 'warning_needs_response', reason: 'fraudulent', createdAt: new Date().toISOString() } }) }); } else { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ orderId: request.orderId, dispute: null }) }); } }); // Mock Firebase authentication await page.addInitScript(() => { // @ts-ignore window.mockUser = { email: 'admin@example.com', organization: { id: 'test-org-1', name: 'Test Organization' }, role: 'admin' }; }); }); test('should display orders table with refund actions', async ({ page }) => { // Mock the orders management page await page.setContent(` Orders Management

Event Orders

Order #test-order-1

$150.00

Customer: test@example.com

Tickets: 2

`); // Verify initial page content await expect(page.locator('h1')).toContainText('Event Orders'); await expect(page.locator('.order-card')).toBeVisible(); await expect(page.locator('.status-badge.paid')).toContainText('Paid'); await expect(page.locator('.amount')).toContainText('$150.00'); // Click refund button to open modal await page.click('#refund-btn'); await expect(page.locator('#refund-modal')).toBeVisible(); await expect(page.locator('#refund-modal h2')).toContainText('Create Refund'); // Test full refund (default) await expect(page.locator('input[value="full"]')).toBeChecked(); await expect(page.locator('#refund-total')).toContainText('$150.00'); // Create full refund await page.click('#confirm-refund'); // Verify success message await expect(page.locator('#refund-result')).toBeVisible(); await expect(page.locator('#refund-result')).toContainText('Refund created successfully'); await expect(page.locator('#refund-result')).toContainText('refund_mock_123'); }); test('should handle partial refund with custom amount', async ({ page }) => { await page.setContent(`
Refund Amount: $150.00
`); // Select partial refund type await page.click('input[value="partial"]'); await expect(page.locator('#custom-amount')).toBeVisible(); // Enter custom amount await page.fill('#refund-amount', '75.50'); await expect(page.locator('#refund-total')).toContainText('$75.50'); // Create partial refund await page.click('#confirm-refund'); // Verify success await expect(page.locator('#refund-result')).toContainText('Success: $75.50 refunded'); }); test('should handle ticket-specific refunds', async ({ page }) => { await page.setContent(`
Refund Amount: $150.00
`); // Select tickets refund type await page.click('input[value="tickets"]'); await expect(page.locator('#ticket-selection')).toBeVisible(); // Select first ticket only await page.click('input[data-ticket-id="ticket-1"]'); await expect(page.locator('#refund-total')).toContainText('$75.00'); // Create single ticket refund await page.click('#confirm-refund'); await expect(page.locator('#refund-result')).toContainText('Ticket refund successful'); }); test('should validate refund amounts and show errors', async ({ page }) => { await page.setContent(`
Refund Amount: $0.00
`); // Test excessive refund amount await page.fill('#refund-amount', '200.00'); await page.click('#confirm-refund'); await expect(page.locator('#refund-result')).toContainText('Error: Invalid refund amount: exceeds order total'); }); test('should display reconciliation report with correct calculations', async ({ page }) => { await page.setContent(`

Reconciliation Report

`); // Verify initial page content await expect(page.locator('h1')).toContainText('Reconciliation Report'); // Load reconciliation data await page.click('#load-data'); // Wait for loading to complete await expect(page.locator('#loading')).toBeVisible(); await expect(page.locator('#summary-cards')).toBeVisible(); // Verify summary calculations await expect(page.locator('#gross-sales')).toContainText('$150.00'); await expect(page.locator('#stripe-fees')).toContainText('$4.65'); await expect(page.locator('#platform-fees')).toContainText('$4.50'); await expect(page.locator('#net-amount')).toContainText('$140.85'); // Verify transactions table await expect(page.locator('#transactions-table')).toBeVisible(); await expect(page.locator('#transactions-body tr')).toHaveCount(3); // 3 ledger entries // Verify export button appears await expect(page.locator('#export-csv')).toBeVisible(); }); test('should handle dispute status display', async ({ page }) => { await page.setContent(`

Order #disputed-order

Dispute VIP Access - $100.00
`); // Load dispute information await page.click('#load-dispute-info'); // Verify dispute alert appears await expect(page.locator('#dispute-alert')).toBeVisible(); await expect(page.locator('#dispute-details')).toContainText('Status: warning_needs_response'); await expect(page.locator('#dispute-details')).toContainText('Reason: fraudulent'); // Verify ticket shows dispute status await expect(page.locator('.ticket-status.locked-dispute')).toContainText('Dispute'); }); test('should validate permission requirements for refunds', async ({ page }) => { // Mock unauthorized request await page.route('**/createRefund', async (route) => { await route.fulfill({ status: 403, contentType: 'application/json', body: JSON.stringify({ error: 'Insufficient permissions' }) }); }); await page.setContent(`
`); await page.click('#create-refund'); await expect(page.locator('#result')).toContainText('Permission denied: Insufficient permissions'); }); }); test.describe('Idempotency and Concurrency', () => { test('should handle duplicate refund requests gracefully', async ({ page }) => { let requestCount = 0; await page.route('**/createRefund', async (route) => { requestCount++; if (requestCount === 1) { // First request - create refund await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ refundId: 'refund_mock_123', status: 'succeeded', amountCents: 5000 }) }); } else { // Subsequent requests - return existing refund await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ refundId: 'refund_mock_123', status: 'succeeded', message: 'Refund already exists' }) }); } }); await page.setContent(`
`); // Click multiple times rapidly await page.click('#create-refund'); await page.click('#create-refund'); await page.click('#create-refund'); // Verify idempotency handling const results = page.locator('#results div'); await expect(results).toHaveCount(3); await expect(results.nth(0)).toContainText('Click 1: Created - refund_mock_123'); await expect(results.nth(1)).toContainText('Click 2: Refund already exists - refund_mock_123'); await expect(results.nth(2)).toContainText('Click 3: Refund already exists - refund_mock_123'); }); });