import { expect, test } from '@playwright/test'; test.describe('PublishEventModal Component', () => { test.beforeEach(async ({ page }) => { // Navigate to events page and set up authenticated state await page.goto('/events'); // Mock authenticated user state await page.evaluate(() => { const mockUser = { id: 'user-1', email: 'organizer@example.com', name: 'Event Organizer', role: 'organizer', organization: { id: 'org-1', name: 'Test Organization', planType: 'pro', payment: { connected: true, accountId: 'acct_test123' }, settings: { theme: 'dark', currency: 'USD', timezone: 'America/New_York' } }, preferences: { theme: 'system', notifications: { email: true, push: true, marketing: false }, dashboard: { defaultView: 'grid', itemsPerPage: 25 } }, metadata: { lastLogin: new Date().toISOString(), createdAt: '2024-01-01T00:00:00Z', emailVerified: true } }; localStorage.setItem('bct_auth_user', JSON.stringify(mockUser)); localStorage.setItem('bct_auth_remember', 'true'); }); // Reload to apply auth state await page.reload(); await page.waitForLoadState('networkidle'); }); test('should display publish button for draft events', async ({ page }) => { // Navigate to a draft event (assuming first event in mock data) await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); // Check for publish button if event is draft const eventStatus = await page.textContent('[data-testid="event-status-badge"]'); if (eventStatus?.toLowerCase().includes('draft')) { await expect(page.locator('button:has-text("Publish Event")')).toBeVisible(); } }); test('should not display publish button for published events', async ({ page }) => { // Set up a published event in mock data await page.evaluate(() => { const eventStore = JSON.parse(localStorage.getItem('event-store') || '{}'); if (eventStore.state?.events) { eventStore.state.events[0] = { ...eventStore.state.events[0], status: 'published', publishedAt: new Date().toISOString() }; localStorage.setItem('event-store', JSON.stringify(eventStore)); } }); await page.reload(); await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); // Publish button should not be visible for published events await expect(page.locator('button:has-text("Publish Event")')).not.toBeVisible(); // But should show published status await expect(page.locator('[data-testid="event-status-badge"]:has-text("published")')).toBeVisible(); }); test('should open publish modal when publish button is clicked', async ({ page }) => { // Navigate to a draft event await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); // Click publish button if it's visible const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); // Check that modal opened await expect(page.locator('[role="dialog"]')).toBeVisible(); await expect(page.locator('[role="dialog"] h2:has-text("Publish Event")')).toBeVisible(); } }); test('should show validation checklist in publish modal', async ({ page }) => { // Navigate to event and open publish modal await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); // Wait for modal to load requirements await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); // Check for validation items await expect(page.locator('text=Active Ticket Types')).toBeVisible(); await expect(page.locator('text=Valid Event Dates')).toBeVisible(); await expect(page.locator('text=Payment Processing')).toBeVisible(); } }); test('should disable publish button when requirements not met', async ({ page }) => { // Set up event with no active ticket types await page.evaluate(() => { const eventStore = JSON.parse(localStorage.getItem('event-store') || '{}'); if (eventStore.state?.ticketTypes) { // Set all ticket types to inactive eventStore.state.ticketTypes.forEach((tt: any) => { tt.status = 'paused'; }); localStorage.setItem('event-store', JSON.stringify(eventStore)); } }); await page.reload(); await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); // Wait for requirements check await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); await page.waitForTimeout(1000); // Wait for requirements loading // Publish button in modal should be disabled const modalPublishButton = page.locator('[role="dialog"] button:has-text("Publish Event")'); await expect(modalPublishButton).toBeDisabled(); } }); test('should show fix action buttons for failed requirements', async ({ page }) => { // Set up event with no active ticket types and disconnect payments await page.evaluate(() => { const eventStore = JSON.parse(localStorage.getItem('event-store') || '{}'); if (eventStore.state?.ticketTypes) { // Set all ticket types to inactive eventStore.state.ticketTypes.forEach((tt: any) => { tt.status = 'paused'; }); localStorage.setItem('event-store', JSON.stringify(eventStore)); } // Disconnect payments const mockUser = JSON.parse(localStorage.getItem('bct_auth_user') || '{}'); mockUser.organization.payment.connected = false; localStorage.setItem('bct_auth_user', JSON.stringify(mockUser)); }); await page.reload(); await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); // Wait for requirements check await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); await page.waitForTimeout(1000); // Should show fix action buttons await expect(page.locator('button:has-text("Add Ticket Type")')).toBeVisible(); await expect(page.locator('button:has-text("Connect Payments")')).toBeVisible(); } }); test('should enable publish button when all requirements are met', async ({ page }) => { // Ensure we have active ticket types and connected payments await page.evaluate(() => { const eventStore = JSON.parse(localStorage.getItem('event-store') || '{}'); if (eventStore.state?.ticketTypes) { // Set at least one ticket type to active eventStore.state.ticketTypes[0].status = 'active'; localStorage.setItem('event-store', JSON.stringify(eventStore)); } // Ensure payments are connected const mockUser = JSON.parse(localStorage.getItem('bct_auth_user') || '{}'); mockUser.organization.payment.connected = true; localStorage.setItem('bct_auth_user', JSON.stringify(mockUser)); }); await page.reload(); await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); // Wait for requirements check await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); await page.waitForTimeout(1000); // Publish button in modal should be enabled const modalPublishButton = page.locator('[role="dialog"] button:has-text("Publish Event")'); await expect(modalPublishButton).toBeEnabled(); } }); test('should close modal when cancel is clicked', async ({ page }) => { await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); // Click cancel button await page.click('[role="dialog"] button:has-text("Cancel")'); // Modal should be closed await expect(page.locator('[role="dialog"]')).not.toBeVisible(); } }); test('should close modal with escape key', async ({ page }) => { await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); // Press escape key await page.keyboard.press('Escape'); // Modal should be closed await expect(page.locator('[role="dialog"]')).not.toBeVisible(); } }); test('should show success message after successful publish', async ({ page }) => { // Set up ideal conditions for publishing await page.evaluate(() => { const eventStore = JSON.parse(localStorage.getItem('event-store') || '{}'); if (eventStore.state?.ticketTypes) { eventStore.state.ticketTypes[0].status = 'active'; localStorage.setItem('event-store', JSON.stringify(eventStore)); } const mockUser = JSON.parse(localStorage.getItem('bct_auth_user') || '{}'); mockUser.organization.payment.connected = true; localStorage.setItem('bct_auth_user', JSON.stringify(mockUser)); }); await page.reload(); await page.click('[data-testid="event-card"]:first-child'); await page.waitForSelector('[data-testid="event-detail-page"]', { timeout: 5000 }); const publishButton = page.locator('button:has-text("Publish Event")'); if (await publishButton.isVisible()) { await publishButton.click(); await page.waitForSelector('[role="dialog"]', { timeout: 3000 }); await page.waitForTimeout(1000); const modalPublishButton = page.locator('[role="dialog"] button:has-text("Publish Event")'); if (await modalPublishButton.isEnabled()) { await modalPublishButton.click(); // Should show success message await expect(page.locator('text=Event Published Successfully!')).toBeVisible(); await expect(page.locator('text=Your event is now live!')).toBeVisible(); } } }); }); test.describe('Publish Event Validation Logic', () => { test('should validate ticket types requirement', async ({ page }) => { await page.goto('/events'); // Mock function to test validation logic const validationResult = await page.evaluate(() => { // Mock ticket types data const ticketTypes = [ { id: '1', eventId: 'evt-1', status: 'active' }, { id: '2', eventId: 'evt-1', status: 'paused' }, ]; // Validation logic: has active ticket types const hasActiveTicketTypes = ticketTypes.some(tt => tt.status === 'active'); return hasActiveTicketTypes; }); expect(validationResult).toBe(true); }); test('should validate no active ticket types', async ({ page }) => { await page.goto('/events'); const validationResult = await page.evaluate(() => { // Mock ticket types with no active ones const ticketTypes = [ { id: '1', eventId: 'evt-1', status: 'paused' }, { id: '2', eventId: 'evt-1', status: 'sold_out' }, ]; const hasActiveTicketTypes = ticketTypes.some(tt => tt.status === 'active'); return hasActiveTicketTypes; }); expect(validationResult).toBe(false); }); test('should validate event dates requirement', async ({ page }) => { await page.goto('/events'); const validationResult = await page.evaluate(() => { // Mock event with valid date const event = { id: 'evt-1', date: '2024-12-31T19:00:00Z', title: 'Test Event' }; // Validation logic: has valid date const hasValidDates = Boolean(event.date); return hasValidDates; }); expect(validationResult).toBe(true); }); test('should validate payment connection requirement', async ({ page }) => { await page.goto('/events'); const validationResult = await page.evaluate(() => { // Mock organization with connected payment const organization = { id: 'org-1', payment: { connected: true, accountId: 'acct_test123' } }; // Validation logic: payment connected const isPaymentConnected = organization.payment?.connected ?? false; return isPaymentConnected; }); expect(validationResult).toBe(true); }); test('should validate all requirements together', async ({ page }) => { await page.goto('/events'); const validationResult = await page.evaluate(() => { // Mock complete scenario const ticketTypes = [{ id: '1', eventId: 'evt-1', status: 'active' }]; const event = { id: 'evt-1', date: '2024-12-31T19:00:00Z' }; const organization = { payment: { connected: true } }; const hasActiveTicketTypes = ticketTypes.some(tt => tt.status === 'active'); const hasValidDates = Boolean(event.date); const isPaymentConnected = organization.payment?.connected ?? false; const canPublish = hasActiveTicketTypes && hasValidDates && isPaymentConnected; return { hasActiveTicketTypes, hasValidDates, isPaymentConnected, canPublish }; }); expect(validationResult.hasActiveTicketTypes).toBe(true); expect(validationResult.hasValidDates).toBe(true); expect(validationResult.isPaymentConnected).toBe(true); expect(validationResult.canPublish).toBe(true); }); test('should fail validation when any requirement is missing', async ({ page }) => { await page.goto('/events'); const validationResult = await page.evaluate(() => { // Mock scenario with missing payment connection const ticketTypes = [{ id: '1', eventId: 'evt-1', status: 'active' }]; const event = { id: 'evt-1', date: '2024-12-31T19:00:00Z' }; const organization = { payment: { connected: false } }; const hasActiveTicketTypes = ticketTypes.some(tt => tt.status === 'active'); const hasValidDates = Boolean(event.date); const isPaymentConnected = organization.payment?.connected ?? false; const canPublish = hasActiveTicketTypes && hasValidDates && isPaymentConnected; return { hasActiveTicketTypes, hasValidDates, isPaymentConnected, canPublish }; }); expect(validationResult.hasActiveTicketTypes).toBe(true); expect(validationResult.hasValidDates).toBe(true); expect(validationResult.isPaymentConnected).toBe(false); expect(validationResult.canPublish).toBe(false); }); });