import { test, expect } from '@playwright/test'; test.describe('CreateTicketTypeModal', () => { test.beforeEach(async ({ page }) => { // Navigate to login and authenticate as admin await page.goto('/login'); await page.fill('[data-testid="email-input"]', 'admin@test.com'); await page.fill('[data-testid="password-input"]', 'password'); await page.click('[data-testid="login-button"]'); // Wait for redirect to dashboard await expect(page).toHaveURL('/dashboard'); // Navigate to a specific event detail page await page.goto('/events/evt-1'); await expect(page.getByTestId('event-detail-page')).toBeVisible(); }); test('should open modal when Add Ticket Type button is clicked', async ({ page }) => { // Click the Add Ticket Type button await page.click('[data-testid="add-ticket-type-button"]'); // Check that modal opens await expect(page.getByRole('dialog')).toBeVisible(); await expect(page.getByRole('heading', { name: 'Create Ticket Type' })).toBeVisible(); }); test('should validate required fields', async ({ page }) => { // Open modal await page.click('[data-testid="add-ticket-type-button"]'); // Try to submit empty form await page.click('button[type="submit"]'); // Check for validation errors await expect(page.getByText('Name must be at least 2 characters')).toBeVisible(); await expect(page.getByText('Price must be greater than 0')).toBeVisible(); }); test('should validate name length constraints', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Test minimum length await page.fill('input[name="name"]', 'A'); await page.click('button[type="submit"]'); await expect(page.getByText('Name must be at least 2 characters')).toBeVisible(); // Test maximum length const longName = 'A'.repeat(61); await page.fill('input[name="name"]', longName); await page.click('button[type="submit"]'); await expect(page.getByText('Name must be 60 characters or less')).toBeVisible(); }); test('should validate price constraints', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill required name field await page.fill('input[name="name"]', 'Test Ticket'); // Test negative price await page.fill('input[type="number"][step="0.01"]', '-10'); await page.click('button[type="submit"]'); await expect(page.getByText('Price cannot be negative')).toBeVisible(); // Test zero price (should be allowed for free tickets) await page.fill('input[type="number"][step="0.01"]', '0'); await page.fill('input[name="inventory"]', '100'); // Should not show price error for free tickets await expect(page.getByText('Price must be greater than 0')).not.toBeVisible(); }); test('should validate inventory constraints', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill required fields await page.fill('input[name="name"]', 'Test Ticket'); await page.fill('input[type="number"][step="0.01"]', '25.00'); // Test negative inventory await page.fill('input[name="inventory"]', '-1'); await page.click('button[type="submit"]'); await expect(page.getByText('Inventory cannot be negative')).toBeVisible(); }); test('should validate sales date constraints', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill required fields await page.fill('input[name="name"]', 'Test Ticket'); await page.fill('input[type="number"][step="0.01"]', '25.00'); await page.fill('input[name="inventory"]', '100'); // Set sales end before sales start await page.fill('input[name="salesStart"]', '2024-12-25T10:00'); await page.fill('input[name="salesEnd"]', '2024-12-20T10:00'); await page.click('button[type="submit"]'); await expect(page.getByText('Sales end date must be after sales start date')).toBeVisible(); }); test('should convert price from dollars to cents correctly', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill form with valid data await page.fill('input[name="name"]', 'General Admission'); await page.fill('input[type="number"][step="0.01"]', '25.99'); await page.fill('input[name="inventory"]', '100'); // Submit form await page.click('button[type="submit"]'); // Check for success message await expect(page.getByText(/created successfully/)).toBeVisible({ timeout: 10000 }); // Modal should close after success await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 3000 }); }); test('should create active ticket type successfully', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill form completely await page.fill('input[name="name"]', 'VIP Access'); await page.fill('input[type="number"][step="0.01"]', '150.00'); await page.fill('input[name="inventory"]', '50'); await page.fill('input[name="salesStart"]', '2024-12-01T09:00'); await page.fill('input[name="salesEnd"]', '2024-12-31T23:59'); // Select active status (should be default) await page.selectOption('[role="combobox"]', 'active'); // Submit form await page.click('button[type="submit"]'); // Wait for success await expect(page.getByText(/VIP Access.*created successfully/)).toBeVisible({ timeout: 10000 }); // Check that new ticket type appears in the list await expect(page.getByText('VIP Access')).toBeVisible(); await expect(page.getByText('$150.00')).toBeVisible(); await expect(page.getByText('0 / 50 sold')).toBeVisible(); }); test('should create paused ticket type successfully', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill form await page.fill('input[name="name"]', 'Early Bird'); await page.fill('input[type="number"][step="0.01"]', '75.00'); await page.fill('input[name="inventory"]', '25'); // Select paused status await page.click('[role="combobox"]'); await page.click('button[role="option"]:has-text("Paused")'); // Submit form await page.click('button[type="submit"]'); // Wait for success await expect(page.getByText(/Early Bird.*created successfully/)).toBeVisible({ timeout: 10000 }); // Check that new ticket type appears with paused status await expect(page.getByText('Early Bird')).toBeVisible(); await expect(page.getByText('paused')).toBeVisible(); }); test('should handle form reset when cancelled', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill form partially await page.fill('input[name="name"]', 'Test Ticket'); await page.fill('input[type="number"][step="0.01"]', '50.00'); // Cancel modal await page.click('button:has-text("Cancel")'); // Modal should close await expect(page.getByRole('dialog')).not.toBeVisible(); // Reopen modal and check form is reset await page.click('[data-testid="add-ticket-type-button"]'); await expect(page.locator('input[name="name"]')).toHaveValue(''); await expect(page.locator('input[type="number"][step="0.01"]')).toHaveValue(''); }); test('should close modal with escape key', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); await expect(page.getByRole('dialog')).toBeVisible(); // Press escape await page.keyboard.press('Escape'); // Modal should close await expect(page.getByRole('dialog')).not.toBeVisible(); }); test('should show permission error for unauthorized users', async ({ page }) => { // Logout and login as regular user (assuming they don't have tickets:write permission) await page.goto('/login'); await page.fill('[data-testid="email-input"]', 'user@test.com'); await page.fill('[data-testid="password-input"]', 'password'); await page.click('[data-testid="login-button"]'); // Navigate to event detail page await page.goto('/events/evt-1'); // Add Ticket Type button should not be visible for regular users await expect(page.getByTestId('add-ticket-type-button')).not.toBeVisible(); }); test('should handle loading states correctly', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill form await page.fill('input[name="name"]', 'Loading Test'); await page.fill('input[type="number"][step="0.01"]', '30.00'); await page.fill('input[name="inventory"]', '75'); // Submit form and check loading state await page.click('button[type="submit"]'); // Submit button should show loading state await expect(page.locator('button[type="submit"]')).toBeDisabled(); // Wait for completion await expect(page.getByText(/created successfully/)).toBeVisible({ timeout: 10000 }); }); test('should create free ticket (price = 0) successfully', async ({ page }) => { await page.click('[data-testid="add-ticket-type-button"]'); // Fill form with free ticket await page.fill('input[name="name"]', 'Free Community Pass'); await page.fill('input[type="number"][step="0.01"]', '0.00'); await page.fill('input[name="inventory"]', '200'); // Submit form await page.click('button[type="submit"]'); // Should succeed without price validation error await expect(page.getByText(/Free Community Pass.*created successfully/)).toBeVisible({ timeout: 10000 }); // Check that ticket appears with $0.00 price await expect(page.getByText('Free Community Pass')).toBeVisible(); await expect(page.getByText('$0.00')).toBeVisible(); }); });