Files
blackcanyontickets/reactrebuild0825/tests/publish-scanner.smoke.spec.ts
dzinesco 8ed7ae95d1 feat: comprehensive project completion and documentation
- Enhanced event creation wizard with multi-step validation
- Added advanced QR scanning system with offline support
- Implemented comprehensive territory management features
- Expanded analytics with export functionality and KPIs
- Created complete design token system with theme switching
- Added 25+ Playwright test files for comprehensive coverage
- Implemented enterprise-grade permission system
- Enhanced component library with 80+ React components
- Added Firebase integration for deployment
- Completed Phase 3 development goals substantially

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 15:04:37 -06:00

354 lines
14 KiB
TypeScript

import * as path from 'path';
import { test, expect } from '@playwright/test';
import type { Page } from '@playwright/test';
/**
* Publish-Scanner Smoke Tests
*
* Critical flow validation for:
* - Event navigation and detail page access
* - Publish logic based on payment connection status (org.payment.connected)
* - Scanner navigation and UI rendering with camera mocking
* - Error state handling and blocked publish flows
* - Screenshots for visual validation
*
* This test suite is designed for CI environments with worker count 1
* and does not require sudo permissions.
*/
async function takeScreenshot(page: Page, name: string) {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = `publish_scanner_${name}_${timestamp}.png`;
await page.screenshot({
path: path.join('screenshots', fileName),
fullPage: true
});
return fileName;
}
async function mockCameraForScanner(page: Page) {
// Mock camera API for scanner testing without requiring real camera access
await page.addInitScript(() => {
// Mock getUserMedia for camera testing
Object.defineProperty(navigator, 'mediaDevices', {
writable: true,
value: {
getUserMedia: () => Promise.resolve({
getTracks: () => [],
addTrack: () => {},
removeTrack: () => {},
getVideoTracks: () => [],
getAudioTracks: () => [],
}),
enumerateDevices: () => Promise.resolve([{
deviceId: 'mock-camera',
kind: 'videoinput',
label: 'Mock Camera',
groupId: 'mock-group'
}])
}
});
});
}
async function loginAs(page: Page, userType: 'admin' | 'organizer') {
await page.goto('/login');
await page.waitForLoadState('networkidle');
// Use demo button method (same as smoke.spec.ts) for reliable login
if (userType === 'admin') {
await page.click('[data-testid="demo-user-sarah-admin"]');
} else {
await page.click('[data-testid="demo-user-john-organizer"]');
}
// Submit form
await page.click('button[type="submit"]');
// Wait for dashboard redirect
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
await page.waitForLoadState('networkidle');
await takeScreenshot(page, `logged-in-as-${userType}`);
}
async function navigateToFirstEvent(page: Page): Promise<string> {
// Navigate to events page
await page.goto('/events');
await page.waitForLoadState('networkidle');
await takeScreenshot(page, 'events-page-loaded');
// Click first event card to go to detail page
const firstEventCard = page.locator('[data-testid="event-card"]').first();
await expect(firstEventCard).toBeVisible({ timeout: 10000 });
await firstEventCard.click();
// Verify we're on event detail page and get event ID from URL
await expect(page.locator('[data-testid="event-detail-page"]')).toBeVisible({ timeout: 10000 });
await takeScreenshot(page, 'event-detail-page-loaded');
// Extract event ID from URL
const currentUrl = page.url();
const eventIdMatch = currentUrl.match(/\/events\/([^\/\?]+)/);
const eventId = eventIdMatch?.[1];
expect(eventId).toBeTruthy();
return eventId!;
}
test.describe('Publish-Scanner Smoke Tests', () => {
test('admin user - payment connected - can publish and access scanner', async ({ page }) => {
// Login as admin (org_001, payment connected)
await loginAs(page, 'admin');
// Navigate to first event detail page
const eventId = await navigateToFirstEvent(page);
// Verify EventDetail header is visible
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('[data-testid="event-detail-page"]')).toBeVisible();
// Test publish logic - admin should be able to publish (payment connected)
const publishButton = page.locator('[data-testid="publishBtn"], [data-testid="publish-event-button"]').first();
if (await publishButton.isVisible()) {
// If publish button exists, it should be enabled (not blocked by payment)
await expect(publishButton).toBeEnabled();
await takeScreenshot(page, 'admin-publish-button-enabled');
// Optional: Click to open publish modal and verify no payment blocking
await publishButton.click();
// Look for publish modal
const publishModal = page.locator('text=Publish Event').first();
if (await publishModal.isVisible()) {
await takeScreenshot(page, 'admin-publish-modal-open');
// Close modal with escape
await page.keyboard.press('Escape');
}
} else {
// Event might already be published
await takeScreenshot(page, 'admin-event-already-published');
}
// Navigate to scanner with camera mocking
await mockCameraForScanner(page);
await page.goto(`/scan?eventId=${eventId}`);
await page.waitForLoadState('networkidle');
// Confirm scanner UI renders
await expect(page.locator('[data-testid="scanner-interface"]')).toBeVisible({ timeout: 15000 });
await takeScreenshot(page, 'admin-scanner-ui-loaded');
// Verify scanner has basic elements
await expect(page.locator('text=Scanner')).toBeVisible();
// Test that camera functionality doesn't crash (mocked)
console.log('✅ Scanner UI loaded successfully with mocked camera for admin user');
});
test('organizer user - payment NOT connected - publish blocked', async ({ page }) => {
// Login as organizer (org_002, payment NOT connected)
await loginAs(page, 'organizer');
// Navigate to first event detail page
const eventId = await navigateToFirstEvent(page);
// Verify EventDetail header is visible
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('[data-testid="event-detail-page"]')).toBeVisible();
// Test publish logic - organizer should be blocked by payment connection
const publishButton = page.locator('[data-testid="publishBtn"], [data-testid="publish-event-button"]').first();
if (await publishButton.isVisible()) {
// Publish button should exist but lead to blocking when clicked
await publishButton.click();
await takeScreenshot(page, 'organizer-publish-button-clicked');
// Look for publish modal
const publishModal = page.locator('text=Publish Event').first();
if (await publishModal.isVisible()) {
// Verify payment processing requirement is failing
const paymentCheck = page.locator('text=Payment Processing').locator('..');
await expect(paymentCheck.locator('[aria-label="Failed"]')).toBeVisible();
// Verify publish button in modal is disabled
const modalPublishButton = page.locator('button:has-text("Publish Event")');
await expect(modalPublishButton).toBeDisabled();
await takeScreenshot(page, 'organizer-publish-blocked-by-payment');
// Close modal
await page.keyboard.press('Escape');
}
}
// Verify payment banner is visible (organizer has disconnected payment)
const paymentBanner = page.locator('[data-testid="payment-banner"]');
if (await paymentBanner.isVisible()) {
await expect(paymentBanner).toBeVisible();
await takeScreenshot(page, 'organizer-payment-banner-visible');
}
// Navigate to scanner (should still work regardless of payment status)
await mockCameraForScanner(page);
await page.goto(`/scan?eventId=${eventId}`);
await page.waitForLoadState('networkidle');
// Confirm scanner UI renders (scanner access not dependent on payment)
await expect(page.locator('[data-testid="scanner-interface"]')).toBeVisible({ timeout: 15000 });
await takeScreenshot(page, 'organizer-scanner-ui-loaded');
// Verify scanner has basic elements
await expect(page.locator('text=Scanner')).toBeVisible();
// Verify scanner works despite payment disconnection
console.log('✅ Scanner UI accessible for organizer despite payment disconnection');
});
test('scanner UI has essential components', async ({ page }) => {
// Login as admin for this scanner-focused test
await loginAs(page, 'admin');
// Navigate to events and get first event ID
const eventId = await navigateToFirstEvent(page);
// Navigate directly to scanner with camera mocking
await mockCameraForScanner(page);
await page.goto(`/scan?eventId=${eventId}`);
await page.waitForLoadState('networkidle');
// Wait for scanner interface to load
await expect(page.locator('[data-testid="scanner-interface"]')).toBeVisible({ timeout: 15000 });
// Check for manual entry functionality (from QR system tests)
const manualEntryButton = page.locator('button[title="Manual Entry"]');
if (await manualEntryButton.isVisible()) {
await expect(manualEntryButton).toBeVisible();
await takeScreenshot(page, 'scanner-manual-entry-available');
// Test opening manual entry modal
await manualEntryButton.click();
await expect(page.locator('h2:text("Manual Entry")')).toBeVisible();
await takeScreenshot(page, 'scanner-manual-entry-modal');
// Close modal
await page.keyboard.press('Escape');
}
// Check for scanner instructions or guidance text
const instructionsVisible = await page.locator('text*=scanner').first().isVisible();
expect(instructionsVisible).toBeTruthy();
await takeScreenshot(page, 'scanner-complete-ui-check');
});
test('event navigation flow validation', async ({ page }) => {
// Test the complete flow without focusing on specific payment status
await loginAs(page, 'admin');
// Start from dashboard, navigate to events
await expect(page).toHaveURL('/dashboard');
// Go to events page
await page.goto('/events');
await expect(page.locator('h1')).toContainText('Events');
await takeScreenshot(page, 'events-page-header-check');
// Verify events are loaded (either cards or empty state)
await expect(page.locator('[data-testid="events-container"]')).toBeVisible();
// Check if we have any event cards
const eventCards = page.locator('[data-testid="event-card"]');
const cardCount = await eventCards.count();
if (cardCount > 0) {
// Click first event card
await eventCards.first().click();
// Verify navigation to event detail
await expect(page).toHaveURL(/\/events\/[^\/]+$/);
await expect(page.locator('[data-testid="event-detail-page"]')).toBeVisible();
await takeScreenshot(page, 'navigation-flow-complete');
} else {
// No events available - this is also a valid state to test
await takeScreenshot(page, 'events-page-empty-state');
}
});
test('scanner direct access with event ID', async ({ page }) => {
// Test direct scanner access without going through event detail page
await loginAs(page, 'admin');
// Use a known event ID (from mock data) or get from events page
const eventId = await navigateToFirstEvent(page);
// Navigate directly to scanner with eventId parameter and camera mocking
await mockCameraForScanner(page);
await page.goto(`/scan?eventId=${eventId}`);
await page.waitForLoadState('networkidle');
// Verify scanner loads with event context
await expect(page.locator('[data-testid="scanner-interface"]')).toBeVisible({ timeout: 15000 });
// Verify the URL contains the event ID
expect(page.url()).toContain(`eventId=${eventId}`);
await takeScreenshot(page, 'scanner-direct-access-with-event-id');
console.log('✅ Direct scanner access with event ID parameter works correctly');
});
test('payment connection status validation', async ({ page }) => {
// Test both payment connected and disconnected scenarios
// Part 1: Admin with payment connected
await loginAs(page, 'admin');
await navigateToFirstEvent(page);
// Check for payment status indicators on event detail page
const paymentDisconnectedBanner = page.locator('[data-testid="payment-banner"]');
// Admin should have payment connected, so no disconnect banner
const hasBanner = await paymentDisconnectedBanner.isVisible();
if (!hasBanner) {
console.log('✅ Admin user: No payment disconnection banner (payment connected)');
await takeScreenshot(page, 'admin-no-payment-banner');
}
// Logout and test organizer
await page.click('[data-testid="user-menu"]');
await page.click('[data-testid="logout-button"]');
await expect(page).toHaveURL('/login', { timeout: 10000 });
// Part 2: Organizer with payment NOT connected
await loginAs(page, 'organizer');
await navigateToFirstEvent(page);
// Organizer should see payment banner or blocking
const organizerBanner = await paymentDisconnectedBanner.isVisible();
if (organizerBanner) {
console.log('✅ Organizer user: Payment disconnection banner visible');
await takeScreenshot(page, 'organizer-payment-status-check');
}
// Try to access publish modal if available
const publishButton = page.locator('[data-testid="publishBtn"], [data-testid="publish-event-button"]').first();
if (await publishButton.isVisible()) {
await publishButton.click();
// Look for payment processing check
const paymentCheckFailed = page.locator('text=Payment Processing').locator('..').locator('[aria-label="Failed"]');
if (await paymentCheckFailed.isVisible()) {
console.log('✅ Publish blocked by payment status for organizer');
await takeScreenshot(page, 'payment-status-blocking-publish');
}
await page.keyboard.press('Escape');
}
});
});