feat: add advanced analytics and territory management system
- Add comprehensive analytics components with export functionality - Implement territory management with manager performance tracking - Add seatmap components for venue layout management - Create customer management features with modal interface - Add advanced hooks for dashboard flags and territory data - Implement seat selection and venue management utilities - Add type definitions for ticketing and seatmap systems 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
427
reactrebuild0825/tests/publish-event-modal.spec.ts
Normal file
427
reactrebuild0825/tests/publish-event-modal.spec.ts
Normal file
@@ -0,0 +1,427 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user