import { test, expect } from '@playwright/test'; // Mock Firebase Firestore data for testing const mockOrgData = { id: 'test-org-1', name: 'Test Organization', payment: { stripe: { accountId: 'acct_test_123', chargesEnabled: true, detailsSubmitted: true, businessName: 'Test Business' } } }; const mockEventData = { id: 'test-event-1', orgId: 'test-org-1', name: 'Test Concert 2024', startAt: new Date('2024-08-01T19:00:00Z'), endAt: new Date('2024-08-01T23:00:00Z'), location: 'Test Venue, Denver CO', status: 'published' }; const mockTicketTypeData = { id: 'test-ticket-type-1', orgId: 'test-org-1', eventId: 'test-event-1', name: 'General Admission', priceCents: 5000, // $50.00 currency: 'USD', inventory: 100, sold: 3 }; const mockSessionId = 'cs_test_session_123'; const mockQrCode = '550e8400-e29b-41d4-a716-446655440000'; test.describe('Stripe Checkout Connected Accounts Flow', () => { test.beforeEach(async ({ page }) => { // Mock network requests to Firebase Functions await page.route('**/createCheckout', async (route) => { const request = await route.request().postDataJSON(); if (request.qty > mockTicketTypeData.inventory - mockTicketTypeData.sold) { await route.fulfill({ status: 400, contentType: 'application/json', body: JSON.stringify({ error: `Not enough tickets available. Requested: ${request.qty}, Available: ${mockTicketTypeData.inventory - mockTicketTypeData.sold}` }) }); return; } await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ url: `https://checkout.stripe.com/c/pay/${mockSessionId}`, sessionId: mockSessionId }) }); }); await page.route('**/getOrder', async (route) => { const request = await route.request().postDataJSON(); if (request.sessionId === mockSessionId) { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ id: mockSessionId, orgId: mockOrgData.id, eventId: mockEventData.id, ticketTypeId: mockTicketTypeData.id, qty: 2, status: 'paid', totalCents: 10000, purchaserEmail: 'test@example.com', eventName: mockEventData.name, ticketTypeName: mockTicketTypeData.name, eventDate: mockEventData.startAt.toISOString(), eventLocation: mockEventData.location, createdAt: new Date().toISOString() }) }); } else { await route.fulfill({ status: 404, contentType: 'application/json', body: JSON.stringify({ error: 'Order not found' }) }); } }); await page.route('**/verifyTicket', async (route) => { const request = await route.request().postDataJSON(); if (request.qr === mockQrCode) { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ valid: true, ticket: { id: 'ticket-123', eventId: mockEventData.id, ticketTypeId: mockTicketTypeData.id, eventName: mockEventData.name, ticketTypeName: mockTicketTypeData.name, status: 'scanned', purchaserEmail: 'test@example.com' } }) }); } else if (request.qr === 'already-scanned-qr') { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ valid: false, reason: 'already_scanned', scannedAt: new Date().toISOString(), ticket: { id: 'ticket-456', eventId: mockEventData.id, ticketTypeId: mockTicketTypeData.id, status: 'scanned' } }) }); } else { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ valid: false, reason: 'Ticket not found' }) }); } }); // Mock Firebase authentication await page.addInitScript(() => { // @ts-ignore window.mockUser = { email: 'test@example.com', organization: { id: 'test-org-1', name: 'Test Organization' } }; }); }); test('should successfully create checkout session and redirect to Stripe', async ({ page }) => { // Mock the checkout page with ticket purchase component await page.setContent(` Test Checkout

Test Concert 2024

General Admission - $50.00

`); // Verify initial page content await expect(page.locator('h1')).toContainText('Test Concert 2024'); await expect(page.locator('#quantity')).toHaveValue('2'); await expect(page.locator('#email')).toHaveValue('test@example.com'); // Test successful checkout creation let redirectUrl = ''; page.on('beforeunload', () => { redirectUrl = page.url(); }); // Intercept the redirect to Stripe Checkout await page.route('https://checkout.stripe.com/**', async (route) => { redirectUrl = route.request().url(); await route.fulfill({ status: 200, contentType: 'text/html', body: '

Stripe Checkout (Mock)

' }); }); await page.click('#purchase-btn'); // Should redirect to Stripe Checkout await page.waitForURL(/checkout\.stripe\.com/); expect(redirectUrl).toContain('checkout.stripe.com'); expect(redirectUrl).toContain(mockSessionId); }); test('should handle insufficient inventory gracefully', async ({ page }) => { await page.setContent(`
`); await page.click('#purchase-btn'); // Should show inventory error await expect(page.locator('#error-display')).toContainText('Not enough tickets available'); await expect(page.locator('#error-display')).toContainText('Requested: 100, Available: 97'); }); test('should display order details on success page', async ({ page }) => { await page.goto(`/checkout/success?session_id=${mockSessionId}`); // Mock the success page await page.setContent(`
Loading...
`); // Should show loading initially await expect(page.locator('#loading')).toBeVisible(); // Should load order details await expect(page.locator('#success-content')).toBeVisible({ timeout: 3000 }); await expect(page.locator('#event-name')).toContainText('Test Concert 2024'); await expect(page.locator('#ticket-type')).toContainText('General Admission'); await expect(page.locator('#quantity')).toContainText('2 tickets'); await expect(page.locator('#email')).toContainText('test@example.com'); await expect(page.locator('#total')).toContainText('$100.00'); }); test('should verify valid tickets correctly', async ({ page }) => { await page.setContent(`

Ticket Verification

`); // Test valid ticket verification await page.fill('#qr-input', mockQrCode); await page.click('#verify-btn'); await expect(page.locator('#result .success')).toBeVisible(); await expect(page.locator('#result')).toContainText('✅ Valid Ticket'); await expect(page.locator('#result')).toContainText('Test Concert 2024'); await expect(page.locator('#result')).toContainText('General Admission'); await expect(page.locator('#result')).toContainText('scanned'); }); test('should handle already scanned tickets', async ({ page }) => { await page.setContent(`
`); await page.fill('#qr-input', 'already-scanned-qr'); await page.click('#verify-btn'); await expect(page.locator('#result')).toContainText('Invalid: already_scanned'); }); test('should handle ticket not found', async ({ page }) => { await page.setContent(`
`); await page.fill('#qr-input', 'nonexistent-qr-code'); await page.click('#verify-btn'); await expect(page.locator('#result')).toContainText('Invalid: Ticket not found'); }); }); test.describe('Idempotency and Concurrency Tests', () => { test('should handle duplicate webhook processing gracefully', async ({ page }) => { // This test would be more relevant for backend testing // Here we simulate the frontend behavior when webhook processing is complete await page.setContent(`
Checking order status...
`); // Should eventually show completed order await expect(page.locator('#status')).toContainText('Order completed successfully', { timeout: 5000 }); }); });