diff --git a/.mcp.json b/.mcp.json index 7bd6361..046b2ad 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,4 +1,7 @@ { + "context": { + "modes": ["sequential-thinking"] + }, "mcpServers": { "supabase": { "command": "npx", @@ -10,6 +13,52 @@ "env": { "SUPABASE_ACCESS_TOKEN": "sbp_d27758bc99df08610f063d2b8964cc0ddd94d00b" } + }, + "stripe": { + "command": "npx", + "args": [ + "-y", + "@stripe/mcp@latest", + "--tools=all" + ], + "env": { + "STRIPE_SECRET_KEY": "${STRIPE_SECRET_KEY}" + } + }, + "ide": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-ide@latest" + ] + }, + "playwright_ui_login": { + "command": "npx", + "args": ["-y", "@playwright/mcp-ui-login@latest"] + }, + "playwright_cookie_restore": { + "command": "npx", + "args": ["-y", "@playwright/mcp-cookie-restore@latest"] + }, + "playwright_check_auth_routes": { + "command": "npx", + "args": ["-y", "@playwright/mcp-check-auth-routes@latest"] + }, + "playwright_multi_role_simulation": { + "command": "npx", + "args": ["-y", "@playwright/mcp-multi-role@latest"] + }, + "playwright_screenshot_compare": { + "command": "npx", + "args": ["-y", "@playwright/mcp-screenshot-compare@latest"] + }, + "playwright_network_inspector": { + "command": "npx", + "args": ["-y", "@playwright/mcp-network-inspector@latest"] + }, + "playwright_trace_debugger": { + "command": "npx", + "args": ["-y", "@playwright/mcp-trace-debugger@latest"] } } } \ No newline at end of file diff --git a/after-add-ticket-click-authenticated.png b/after-add-ticket-click-authenticated.png new file mode 100644 index 0000000..f0d433f Binary files /dev/null and b/after-add-ticket-click-authenticated.png differ diff --git a/after-create-first-authenticated.png b/after-create-first-authenticated.png new file mode 100644 index 0000000..3928b36 Binary files /dev/null and b/after-create-first-authenticated.png differ diff --git a/after-ticket-types-load.png b/after-ticket-types-load.png new file mode 100644 index 0000000..f437685 Binary files /dev/null and b/after-ticket-types-load.png differ diff --git a/calendar-diagnosis.png b/calendar-diagnosis.png new file mode 100644 index 0000000..d8b72b9 Binary files /dev/null and b/calendar-diagnosis.png differ diff --git a/dashboard-debug.png b/dashboard-debug.png new file mode 100644 index 0000000..3cec00a Binary files /dev/null and b/dashboard-debug.png differ diff --git a/homepage-access.png b/homepage-access.png new file mode 100644 index 0000000..9e51a36 Binary files /dev/null and b/homepage-access.png differ diff --git a/homepage-debug.png b/homepage-debug.png new file mode 100644 index 0000000..224a923 Binary files /dev/null and b/homepage-debug.png differ diff --git a/login-before-auth.png b/login-before-auth.png new file mode 100644 index 0000000..bceea0a Binary files /dev/null and b/login-before-auth.png differ diff --git a/login-failed.png b/login-failed.png new file mode 100644 index 0000000..ad8b52d Binary files /dev/null and b/login-failed.png differ diff --git a/login-page.png b/login-page.png index 332a955..f90e9d2 100644 Binary files a/login-page.png and b/login-page.png differ diff --git a/manage-page-authenticated.png b/manage-page-authenticated.png new file mode 100644 index 0000000..21456df Binary files /dev/null and b/manage-page-authenticated.png differ diff --git a/manage-page-debug.png b/manage-page-debug.png new file mode 100644 index 0000000..21c6685 Binary files /dev/null and b/manage-page-debug.png differ diff --git a/manage-page-dev.png b/manage-page-dev.png new file mode 100644 index 0000000..cdee28f Binary files /dev/null and b/manage-page-dev.png differ diff --git a/manage-page.png b/manage-page.png new file mode 100644 index 0000000..3cec00a Binary files /dev/null and b/manage-page.png differ diff --git a/modal-light-mode-test.png b/modal-light-mode-test.png new file mode 100644 index 0000000..31dcba9 Binary files /dev/null and b/modal-light-mode-test.png differ diff --git a/package.json b/package.json index cbe0606..6d03acd 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,11 @@ "docker:astro:down": "docker-compose -f docker-compose.astro.yml down", "docker:astro:logs": "docker-compose -f docker-compose.astro.yml logs -f", "docker:dev": "docker-compose up -d", - "docker:dev:build": "docker-compose up -d --build" + "docker:dev:build": "docker-compose up -d --build", + "cache:clear": "./scripts/clear-cache.sh", + "cache:clear:hard": "./scripts/clear-cache.sh && npm run docker:build --no-cache && npm run docker:up", + "dev:clean": "./scripts/clear-cache.sh && npm run dev", + "build:clean": "./scripts/clear-cache.sh && npm run build" }, "dependencies": { "@astrojs/check": "^0.9.4", diff --git a/playwright-report/data/2f123d5d0441b10a37eb4e2408d867c110d358c9.png b/playwright-report/data/2f123d5d0441b10a37eb4e2408d867c110d358c9.png new file mode 100644 index 0000000..4d11bd7 Binary files /dev/null and b/playwright-report/data/2f123d5d0441b10a37eb4e2408d867c110d358c9.png differ diff --git a/playwright-report/data/bb040e8c5b8b0ba7a333a6565559f2cf719f5cbc.md b/playwright-report/data/bb040e8c5b8b0ba7a333a6565559f2cf719f5cbc.md new file mode 100644 index 0000000..b045359 --- /dev/null +++ b/playwright-report/data/bb040e8c5b8b0ba7a333a6565559f2cf719f5cbc.md @@ -0,0 +1,122 @@ +# Page snapshot + +```yaml +- link "Skip to main content": + - /url: "#main-content" +- link "Skip to navigation": + - /url: "#navigation" +- main: + - navigation: + - link "BCT": + - /url: /dashboard + - img + - text: BCT + - link "All Events": + - /url: /dashboard + - img + - text: All Events + - text: "| Event Management" + - button "Switch to dark mode": + - img + - button "T Tyler Admin": + - text: T Tyler Admin + - img + - main: + - heading "Garfield County Fair & Rodeo 2025" [level=1] + - img + - text: Garfield County Fairgrounds - Rifle, CO + - img + - text: Wednesday, August 6, 2025 at 12:00 AM + - paragraph: Secure your spot at the 87-year-strong Garfield County Fair & Rodeo, returning to Rifle, Colorado August 2 and 6-9, 2025... + - button "Show more" + - text: $0 Total Revenue + - link "Preview": + - /url: /e/firebase-event-zmkx63pewurqyhtw6tsn + - img + - text: Preview + - button "Embed": + - img + - text: Embed + - button "Edit": + - img + - text: Edit + - link "Scanner": + - /url: /scan + - img + - text: Scanner + - link "Kiosk": + - /url: /kiosk/firebase-event-zmkx63pewurqyhtw6tsn + - img + - text: Kiosk + - button "PIN": + - img + - text: PIN + - paragraph: Tickets Sold + - paragraph: "0" + - img + - text: +12% from last week + - img + - paragraph: Available + - paragraph: "0" + - img + - text: Ready to sell + - img + - paragraph: Check-ins + - paragraph: "0" + - img + - text: 85% attendance rate + - img + - paragraph: Net Revenue + - paragraph: $0 + - img + - text: +24% this month + - img + - button "Ticketing & Access": + - img + - text: Ticketing & Access + - button "Custom Pages": + - img + - text: Custom Pages + - button "Sales": + - img + - text: Sales + - button "Attendees": + - img + - text: Attendees + - button "Event Settings": + - img + - text: Event Settings + - button "Ticket Types" + - button "Access Codes" + - button "Discounts" + - button "Printed Tickets" + - heading "Ticket Types & Pricing" [level=2] + - button: + - img + - button: + - img + - button "Add Ticket Type": + - img + - text: Add Ticket Type + - img + - paragraph: No ticket types created yet + - button "Create Your First Ticket Type" +- contentinfo: + - link "Terms of Service": + - /url: /terms + - link "Privacy Policy": + - /url: /privacy + - link "Support": + - /url: /support + - text: © 2025 All rights reserved Powered by + - link "blackcanyontickets.com": + - /url: https://blackcanyontickets.com +- img +- heading "Cookie Preferences" [level=3] +- paragraph: + - text: We use essential cookies to make our website work and analytics cookies to understand how you interact with our site. + - link "Learn more in our Privacy Policy": + - /url: /privacy +- button "Manage Preferences" +- button "Accept All" +``` \ No newline at end of file diff --git a/playwright-report/index.html b/playwright-report/index.html index 79e99c3..f7df93c 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -74,4 +74,4 @@ Error generating stack: `+u.message+` \ No newline at end of file +window.playwrightReportBase64 = "data:application/zip;base64,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"; \ No newline at end of file diff --git a/playwright.config.js b/playwright.config.js index 41b3c92..240595b 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -1,9 +1,9 @@ // @ts-check -const { defineConfig, devices } = require('@playwright/test'); +import { defineConfig, devices } from '@playwright/test'; -module.exports = defineConfig({ +export default defineConfig({ testDir: './', - testMatch: 'test-calendar-theme.cjs', + testMatch: 'test-*.cjs', fullyParallel: false, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, @@ -20,9 +20,9 @@ module.exports = defineConfig({ use: { ...devices['Desktop Chrome'] }, } ], - webServer: { - command: 'echo "Server assumed to be running"', - url: 'http://localhost:3000', - reuseExistingServer: true, - }, + // webServer: { + // command: 'echo "Server assumed to be running"', + // url: 'http://192.168.0.46:3000', + // reuseExistingServer: true, + // }, }); \ No newline at end of file diff --git a/src/components/EmbedModalWrapper.tsx b/src/components/EmbedModalWrapper.tsx new file mode 100644 index 0000000..ace5b66 --- /dev/null +++ b/src/components/EmbedModalWrapper.tsx @@ -0,0 +1,49 @@ +import React, { useState, useEffect } from 'react'; +import EmbedCodeModal from './modals/EmbedCodeModal.tsx'; + +interface EmbedModalWrapperProps { + eventId: string; +} + +// Global state interface +declare global { + interface Window { + embedModalState?: { + isOpen: boolean; + eventId: string; + eventSlug: string; + }; + openEmbedModal?: (eventId: string, eventSlug: string) => void; + } +} + +export default function EmbedModalWrapper({ eventId }: EmbedModalWrapperProps) { + const [isOpen, setIsOpen] = useState(false); + const [eventSlug, setEventSlug] = useState('loading'); + + useEffect(() => { + // Function to open modal from JavaScript + window.openEmbedModal = (eventId: string, eventSlug: string) => { + setEventSlug(eventSlug); + setIsOpen(true); + }; + + // Cleanup function + return () => { + delete window.openEmbedModal; + }; + }, []); + + const handleClose = () => { + setIsOpen(false); + }; + + return ( + + ); +} \ No newline at end of file diff --git a/src/components/EventHeader.astro b/src/components/EventHeader.astro index 5a4942a..994d406 100644 --- a/src/components/EventHeader.astro +++ b/src/components/EventHeader.astro @@ -1,4 +1,6 @@ --- +import SimpleEmbedTest from './SimpleEmbedTest.tsx'; + interface Props { eventId: string; } @@ -118,6 +120,12 @@ const { eventId } = Astro.props; + + + \ No newline at end of file diff --git a/src/pages/events/[id]/manage.astro b/src/pages/events/[id]/manage.astro index feb80c6..dee6ae1 100644 --- a/src/pages/events/[id]/manage.astro +++ b/src/pages/events/[id]/manage.astro @@ -11,7 +11,9 @@ import { verifyAuth } from '../../../lib/auth'; // Server-side authentication check using cookies const auth = await verifyAuth(Astro.cookies); if (!auth) { - return Astro.redirect('/login-new'); + // Store the current URL to redirect back after login + const currentUrl = Astro.url.pathname; + return Astro.redirect(`/login-new?redirect=${encodeURIComponent(currentUrl)}`); } // Get event ID from URL parameters diff --git a/src/pages/events/new.astro b/src/pages/events/new.astro index 2ad74be..8004acd 100644 --- a/src/pages/events/new.astro +++ b/src/pages/events/new.astro @@ -6,8 +6,8 @@ import { verifyAuth } from '../../lib/auth'; // Enable server-side rendering for auth checks export const prerender = false; -// Server-side authentication check -const auth = await verifyAuth(Astro.request); +// Server-side auth check using cookies for better SSR compatibility +const auth = await verifyAuth(Astro.cookies); if (!auth) { return Astro.redirect('/login-new'); } @@ -324,26 +324,39 @@ if (!auth) { // Load user data (auth already verified server-side) async function loadUserData() { - const { data: { user: authUser } } = await supabase.auth.getUser(); - - if (!authUser) { - // Silently handle client-side auth failure - user might be logged out + try { + // Try getSession first, then getUser as fallback + const { data: session } = await supabase.auth.getSession(); + let authUser = session?.user; + + if (!authUser) { + const { data: { user: userData } } = await supabase.auth.getUser(); + authUser = userData; + } + + if (!authUser) { + // Silently handle client-side auth failure - user might be logged out + window.location.href = '/login-new'; + return null; + } + + // Get user details + const { data: user } = await supabase + .from('users') + .select('name, email, organization_id, role') + .eq('id', authUser.id) + .single(); + + if (user) { + currentOrganizationId = user.organization_id; + } + + return authUser; + } catch (error) { + console.error('Auth error:', error); window.location.href = '/login-new'; return null; } - - // Get user details - const { data: user } = await supabase - .from('users') - .select('name, email, organization_id, role') - .eq('id', authUser.id) - .single(); - - if (user) { - currentOrganizationId = user.organization_id; - } - - return authUser; } // Generate slug from title @@ -505,7 +518,15 @@ if (!auth) { .select() .single(); - if (eventError) throw eventError; + if (eventError) { + console.error('Event creation error:', eventError); + throw eventError; + } + + if (!event) { + console.error('Event creation returned null data'); + throw new Error('Event creation failed - no data returned'); + } // Premium add-ons will be handled in future updates @@ -513,8 +534,22 @@ if (!auth) { window.location.href = `/events/${event.id}/manage`; } catch (error) { - // Handle errors gracefully without exposing details - errorMessage.textContent = 'An error occurred creating the event. Please try again.'; + console.error('Event creation error:', error); + + // Show specific error message if available + let message = 'An error occurred creating the event. Please try again.'; + + if (error instanceof Error) { + if (error.message?.includes('slug')) { + message = 'An event with this title already exists. Please choose a different title.'; + } else if (error.message?.includes('organization')) { + message = 'Organization access error. Please try logging out and back in.'; + } else if (error.message?.includes('venue')) { + message = 'Please select or enter a venue for your event.'; + } + } + + errorMessage.textContent = message; errorMessage.classList.remove('hidden'); } }); diff --git a/src/pages/login-new.astro b/src/pages/login-new.astro index a062127..444dc84 100644 --- a/src/pages/login-new.astro +++ b/src/pages/login-new.astro @@ -130,8 +130,10 @@ import LoginLayout from '../layouts/LoginLayout.astro'; const data = await response.json(); if (response.ok && data.success) { - // Login successful, redirect to dashboard - window.location.href = data.redirectTo || '/dashboard'; + // Login successful, redirect to intended destination or dashboard + const urlParams = new URLSearchParams(window.location.search); + const redirectTo = urlParams.get('redirect') || data.redirectTo || '/dashboard'; + window.location.href = redirectTo; } else { // Show error message errorMessage.textContent = data.error || 'Login failed. Please try again.'; diff --git a/src/styles/glassmorphism.css b/src/styles/glassmorphism.css index 7a7556f..62facba 100644 --- a/src/styles/glassmorphism.css +++ b/src/styles/glassmorphism.css @@ -1176,6 +1176,31 @@ nav a:hover { border: 1px solid var(--error-border); } +/* Ticket Status Badge Classes */ +.ticket-status-checked-in { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + color: var(--success-color); + background: var(--success-bg); + border: 1px solid var(--success-border); +} + +.ticket-status-pending { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + color: var(--warning-color); + background: var(--warning-bg); + border: 1px solid var(--warning-border); +} + /* Range Slider Styling for Glassmorphism */ .slider-thumb { -webkit-appearance: none; diff --git a/test-buttons-auth.cjs b/test-buttons-auth.cjs new file mode 100644 index 0000000..c4329d4 --- /dev/null +++ b/test-buttons-auth.cjs @@ -0,0 +1,132 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Button Functionality with Auth Bypass', () => { + test('should test buttons with direct component access', async ({ page }) => { + console.log('Testing button functionality...'); + + // Navigate directly to manage page and check what happens + await page.goto('http://localhost:3000/events/test/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + const currentUrl = page.url(); + console.log(`Current URL: ${currentUrl}`); + + // Take screenshot + await page.screenshot({ path: 'manage-page-debug.png', fullPage: true }); + + // Check what's actually loaded on the page + const pageContent = await page.evaluate(() => { + return { + title: document.title, + hasReactScript: !!document.querySelector('script[src*="react"]'), + hasAstroScript: !!document.querySelector('script[src*="astro"]'), + hasEmbedModalWrapper: !!document.querySelector('embed-modal-wrapper'), + reactRoots: document.querySelectorAll('[data-reactroot]').length, + allScripts: Array.from(document.querySelectorAll('script[src]')).map(s => s.src.split('/').pop()), + embedButton: !!document.getElementById('embed-code-btn'), + hasClientLoad: !!document.querySelector('[data-astro-cid]'), + bodyHTML: document.body.innerHTML.substring(0, 1000) // First 1000 chars + }; + }); + + console.log('Page analysis:', JSON.stringify(pageContent, null, 2)); + + // Check for console errors during page load + const errors = []; + page.on('console', msg => { + if (msg.type() === 'error') { + errors.push(msg.text()); + console.log('Console error:', msg.text()); + } + }); + + page.on('pageerror', error => { + errors.push(error.message); + console.log('Page error:', error.message); + }); + + // Try to directly access a manage page that might not require auth + await page.goto('http://localhost:3000/dashboard'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + console.log('Dashboard URL:', page.url()); + await page.screenshot({ path: 'dashboard-debug.png', fullPage: true }); + + // Check if we can access any page that has buttons + await page.goto('http://localhost:3000/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + console.log('Homepage URL:', page.url()); + await page.screenshot({ path: 'homepage-debug.png', fullPage: true }); + + // Look for any buttons on homepage + const homeButtons = await page.locator('button').count(); + console.log(`Homepage buttons found: ${homeButtons}`); + + if (homeButtons > 0) { + const buttonTexts = await page.locator('button').allTextContents(); + console.log('Homepage button texts:', buttonTexts); + } + + // Check for React components on homepage + const homeReactInfo = await page.evaluate(() => { + return { + reactElements: document.querySelectorAll('[data-reactroot], [data-react-class]').length, + clientLoadElements: document.querySelectorAll('[client\\:load]').length, + astroIslands: document.querySelectorAll('astro-island').length, + embedModalWrapper: !!document.querySelector('embed-modal-wrapper') + }; + }); + + console.log('Homepage React info:', homeReactInfo); + + console.log('All errors found:', errors); + }); + + test('should check specific component loading issues', async ({ page }) => { + console.log('Checking component loading...'); + + // Navigate to homepage first + await page.goto('http://localhost:3000/'); + await page.waitForLoadState('networkidle'); + + // Wait for potential React hydration + await page.waitForTimeout(5000); + + // Check if React is loaded + const reactStatus = await page.evaluate(() => { + return { + hasReact: typeof React !== 'undefined', + hasReactDOM: typeof ReactDOM !== 'undefined', + windowKeys: Object.keys(window).filter(key => key.toLowerCase().includes('react')), + astroIslands: Array.from(document.querySelectorAll('astro-island')).map(island => ({ + component: island.getAttribute('component-export'), + clientDirective: island.getAttribute('client'), + props: island.getAttribute('props') + })) + }; + }); + + console.log('React status:', JSON.stringify(reactStatus, null, 2)); + + // Try to manually test if we can create the embed modal + const manualTest = await page.evaluate(() => { + // Try to simulate what should happen when embed button is clicked + const embedBtn = document.getElementById('embed-code-btn'); + if (embedBtn) { + console.log('Found embed button'); + return { + embedButtonExists: true, + hasClickListener: embedBtn.onclick !== null, + buttonHTML: embedBtn.outerHTML + }; + } + return { embedButtonExists: false }; + }); + + console.log('Manual embed test:', manualTest); + }); +}); \ No newline at end of file diff --git a/test-buttons.cjs b/test-buttons.cjs new file mode 100644 index 0000000..180003d --- /dev/null +++ b/test-buttons.cjs @@ -0,0 +1,156 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Button Functionality Tests', () => { + test('should test ticket pricing buttons', async ({ page }) => { + console.log('Starting button functionality test...'); + + // Navigate to a manage page (assuming there's a test event) + // First, let's try to login and then go to manage page + await page.goto('http://localhost:3000/login-new'); + await page.waitForLoadState('networkidle'); + + // Take screenshot of login page + await page.screenshot({ path: 'login-page.png', fullPage: true }); + console.log('Login page screenshot saved'); + + // Check if we can find any buttons on the current page + const buttons = await page.locator('button').count(); + console.log(`Found ${buttons} buttons on login page`); + + // Try to navigate directly to manage page (might redirect if not authenticated) + await page.goto('http://localhost:3000/events/test/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); // Wait for any React components to load + + // Take screenshot of manage page + await page.screenshot({ path: 'manage-page.png', fullPage: true }); + console.log('Manage page screenshot saved'); + + // Check if we're redirected to login + const currentUrl = page.url(); + console.log(`Current URL: ${currentUrl}`); + + if (currentUrl.includes('/login')) { + console.log('Redirected to login - need authentication'); + return; + } + + // Look for ticket pricing buttons + const addTicketButton = page.locator('button').filter({ hasText: 'Add Ticket Type' }); + const createFirstTicketButton = page.locator('button').filter({ hasText: 'Create Your First Ticket Type' }); + const embedButton = page.locator('button#embed-code-btn'); + + console.log('Checking for ticket buttons...'); + + // Count all buttons on manage page + const allButtons = await page.locator('button').count(); + console.log(`Found ${allButtons} total buttons on manage page`); + + // Get all button texts + const buttonTexts = await page.locator('button').allTextContents(); + console.log('Button texts found:', buttonTexts); + + // Test embed button + const embedButtonExists = await embedButton.count(); + console.log(`Embed button exists: ${embedButtonExists > 0}`); + + if (embedButtonExists > 0) { + console.log('Testing embed button click...'); + + // Click embed button and check for modal + await embedButton.click(); + await page.waitForTimeout(1000); + + // Look for embed modal + const modal = page.locator('[data-testid="embed-modal"], .modal, [role="dialog"]'); + const modalExists = await modal.count(); + console.log(`Modal appeared after embed button click: ${modalExists > 0}`); + + // Take screenshot after button click + await page.screenshot({ path: 'after-embed-click.png', fullPage: true }); + console.log('Screenshot after embed button click saved'); + + // Check console errors + const consoleMessages = []; + page.on('console', msg => { + if (msg.type() === 'error') { + consoleMessages.push(msg.text()); + } + }); + + console.log('Console errors:', consoleMessages); + } + + // Test ticket pricing buttons if they exist + const addTicketExists = await addTicketButton.count(); + console.log(`Add Ticket Type button exists: ${addTicketExists > 0}`); + + if (addTicketExists > 0) { + console.log('Testing Add Ticket Type button...'); + await addTicketButton.first().click(); + await page.waitForTimeout(1000); + + // Look for ticket modal + const ticketModal = page.locator('[data-testid="ticket-modal"], .modal, [role="dialog"]'); + const ticketModalExists = await ticketModal.count(); + console.log(`Ticket modal appeared: ${ticketModalExists > 0}`); + + await page.screenshot({ path: 'after-ticket-button-click.png', fullPage: true }); + console.log('Screenshot after ticket button click saved'); + } + + // Also test "Create Your First Ticket Type" button + const createFirstExists = await createFirstTicketButton.count(); + console.log(`Create First Ticket button exists: ${createFirstExists > 0}`); + + if (createFirstExists > 0) { + console.log('Testing Create Your First Ticket Type button...'); + await createFirstTicketButton.click(); + await page.waitForTimeout(1000); + + await page.screenshot({ path: 'after-create-first-ticket-click.png', fullPage: true }); + console.log('Screenshot after create first ticket click saved'); + } + }); + + test('should check for JavaScript errors', async ({ page }) => { + const errors = []; + page.on('console', msg => { + if (msg.type() === 'error') { + errors.push(msg.text()); + } + }); + + page.on('pageerror', error => { + errors.push(error.message); + }); + + await page.goto('http://localhost:3000/events/test/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(3000); // Wait for all JS to load + + console.log('JavaScript errors found:', errors); + + // Try to access window.openEmbedModal + const hasOpenEmbedModal = await page.evaluate(() => { + return typeof window.openEmbedModal === 'function'; + }); + + console.log('window.openEmbedModal function exists:', hasOpenEmbedModal); + + // Check if React components are mounted + const reactComponents = await page.evaluate(() => { + const components = []; + const elements = document.querySelectorAll('[data-reactroot], [data-react-class]'); + components.push(`React elements found: ${elements.length}`); + + // Check for our specific components + const embedWrapper = document.querySelector('embed-modal-wrapper, [class*="EmbedModal"]'); + components.push(`EmbedModalWrapper found: ${!!embedWrapper}`); + + return components; + }); + + console.log('React component status:', reactComponents); + }); +}); \ No newline at end of file diff --git a/test-calendar-diagnosis.cjs b/test-calendar-diagnosis.cjs new file mode 100644 index 0000000..ed9ee03 --- /dev/null +++ b/test-calendar-diagnosis.cjs @@ -0,0 +1,106 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Calendar Page Diagnosis', () => { + test('should open calendar page and take screenshot', async ({ page }) => { + console.log('Opening calendar page...'); + + // Navigate to calendar page + await page.goto('http://localhost:3000/calendar'); + + // Wait for page to load + await page.waitForLoadState('networkidle'); + + // Take a full page screenshot + await page.screenshot({ + path: 'calendar-diagnosis.png', + fullPage: true + }); + + console.log('Screenshot saved as calendar-diagnosis.png'); + + // Get page title + const title = await page.title(); + console.log('Page title:', title); + + // Check if navigation is present + const navigation = await page.$('nav'); + console.log('Navigation present:', navigation !== null); + + // Look for Create Event button/link + const createEventLinks = await page.$$('a[href="/events/new"]'); + console.log('Create Event links found:', createEventLinks.length); + + // Look for any manage links + const manageLinks = await page.$$('a[href*="manage"]'); + console.log('Manage links found:', manageLinks.length); + + // Check if user dropdown exists + const userDropdown = await page.$('#user-dropdown'); + console.log('User dropdown present:', userDropdown !== null); + + // Check if user menu button exists + const userMenuBtn = await page.$('#user-menu-btn'); + console.log('User menu button present:', userMenuBtn !== null); + + // If user menu exists, click it to see dropdown + if (userMenuBtn) { + console.log('Clicking user menu button...'); + await userMenuBtn.click(); + + // Wait a bit for dropdown to appear + await page.waitForTimeout(500); + + // Take another screenshot with dropdown open + await page.screenshot({ + path: 'calendar-diagnosis-dropdown.png', + fullPage: true + }); + + console.log('Dropdown screenshot saved as calendar-diagnosis-dropdown.png'); + + // Check what links are in the dropdown + const dropdownLinks = await page.$$('#user-dropdown a'); + console.log('Dropdown links found:', dropdownLinks.length); + + for (let i = 0; i < dropdownLinks.length; i++) { + const link = dropdownLinks[i]; + const href = await link.getAttribute('href'); + const text = await link.textContent(); + console.log(`Dropdown link ${i + 1}: "${text}" -> ${href}`); + } + } + + // Get current URL to confirm where we are + const currentUrl = page.url(); + console.log('Current URL:', currentUrl); + + // Check console errors + const consoleMessages = []; + page.on('console', msg => { + consoleMessages.push(`${msg.type()}: ${msg.text()}`); + }); + + // Check for any JavaScript errors + const errors = []; + page.on('pageerror', error => { + errors.push(error.message); + }); + + // Refresh page to catch any console messages + await page.reload(); + await page.waitForLoadState('networkidle'); + + console.log('Console messages:', consoleMessages); + console.log('JavaScript errors:', errors); + + // Check if authentication is working + try { + const response = await page.evaluate(() => { + return fetch('/api/auth/user').then(r => r.json()); + }); + console.log('Auth status:', response); + } catch (error) { + console.log('Auth check failed:', error.message); + } + }); +}); \ No newline at end of file diff --git a/test-dark-mode-modal.cjs b/test-dark-mode-modal.cjs new file mode 100644 index 0000000..fe6a976 --- /dev/null +++ b/test-dark-mode-modal.cjs @@ -0,0 +1,171 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Dark Mode Modal Testing', () => { + test('should test modal in dark mode with proper opacity', async ({ page }) => { + console.log('Testing modal in dark mode...'); + + // First, go to login page + await page.goto('http://localhost:3001/login-new'); + await page.waitForLoadState('networkidle'); + + // Login + await page.fill('input[type="email"], input[name="email"]', 'tmartinez@gmail.com'); + await page.fill('input[type="password"], input[name="password"]', 'TestPassword123!'); + await page.click('button[type="submit"], button:has-text("Sign In"), button:has-text("Login")'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(3000); + + if (page.url().includes('/login')) { + console.log('Login failed, skipping test'); + return; + } + + // Navigate to manage page + await page.goto('http://localhost:3001/events/d89dd7ec-01b3-4b89-a52e-b08afaf7661d/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(5000); + + // Click on Ticket Types tab + const ticketTypesTab = page.locator('button').filter({ hasText: /Ticket Types/i }); + if (await ticketTypesTab.count() > 0) { + await ticketTypesTab.first().click(); + await page.waitForTimeout(3000); + } + + // Test in light mode first + console.log('Testing modal in light mode...'); + const addTicketButton = page.locator('button').filter({ hasText: /Add Ticket Type/i }); + + if (await addTicketButton.count() > 0) { + await addTicketButton.first().click(); + await page.waitForTimeout(2000); + + // Check modal background opacity in light mode + const lightModeOpacity = await page.evaluate(() => { + const modal = document.querySelector('[style*="background: rgba(0, 0, 0, 0.75)"]'); + if (modal) { + const style = window.getComputedStyle(modal); + return { + background: style.background || modal.style.background, + isVisible: modal.offsetParent !== null, + zIndex: style.zIndex + }; + } + return null; + }); + + console.log('Light mode modal background:', lightModeOpacity); + + // Close modal by clicking X button + const closeButton = page.locator('button[aria-label="Close modal"]'); + if (await closeButton.count() > 0) { + console.log('Testing X button close functionality...'); + await closeButton.first().click(); + await page.waitForTimeout(1000); + + // Verify modal is closed + const modalClosed = await page.evaluate(() => { + const modal = document.querySelector('[style*="background: rgba(0, 0, 0, 0.75)"]'); + return !modal || modal.offsetParent === null; + }); + + console.log('Modal closed after X button click:', modalClosed); + } + + await page.screenshot({ path: 'modal-light-mode-test.png', fullPage: true }); + } + + // Switch to dark mode - look for theme toggle + console.log('Switching to dark mode...'); + const themeToggle = page.locator('button').filter({ hasText: /theme|dark|light/i }).first(); + const themeToggleIcon = page.locator('button svg').first(); // Often theme toggles are just icons + + // Try different approaches to toggle dark mode + if (await themeToggle.count() > 0) { + await themeToggle.click(); + } else if (await themeToggleIcon.count() > 0) { + await themeToggleIcon.click(); + } else { + // Try to find theme toggle by looking for common selectors + const possibleToggles = await page.locator('button, [role="button"]').all(); + for (const toggle of possibleToggles) { + const title = await toggle.getAttribute('title').catch(() => null); + const ariaLabel = await toggle.getAttribute('aria-label').catch(() => null); + if (title?.includes('theme') || title?.includes('dark') || + ariaLabel?.includes('theme') || ariaLabel?.includes('dark')) { + await toggle.click(); + break; + } + } + } + + await page.waitForTimeout(1000); + + // Test modal in dark mode + console.log('Testing modal in dark mode...'); + if (await addTicketButton.count() > 0) { + await addTicketButton.first().click(); + await page.waitForTimeout(2000); + + // Check modal background opacity in dark mode + const darkModeOpacity = await page.evaluate(() => { + const modal = document.querySelector('[style*="background: rgba(0, 0, 0, 0.75)"]'); + if (modal) { + const style = window.getComputedStyle(modal); + return { + background: style.background || modal.style.background, + isVisible: modal.offsetParent !== null, + zIndex: style.zIndex, + modalContent: modal.innerHTML.length > 0 + }; + } + return null; + }); + + console.log('Dark mode modal background:', darkModeOpacity); + + // Test close button in dark mode + const closeButtonDark = page.locator('button[aria-label="Close modal"]'); + if (await closeButtonDark.count() > 0) { + console.log('Testing X button in dark mode...'); + await closeButtonDark.first().click(); + await page.waitForTimeout(1000); + + const modalClosedDark = await page.evaluate(() => { + const modal = document.querySelector('[style*="background: rgba(0, 0, 0, 0.75)"]'); + return !modal || modal.offsetParent === null; + }); + + console.log('Dark mode modal closed after X button click:', modalClosedDark); + } + + await page.screenshot({ path: 'modal-dark-mode-test.png', fullPage: true }); + } + + // Final verification - check if background opacity is correct (0.75) + const opacityVerification = await page.evaluate(() => { + // Look for any modals with the expected opacity + const modalSelectors = [ + '[style*="rgba(0, 0, 0, 0.75)"]', + '[style*="background: rgba(0, 0, 0, 0.75)"]', + '.fixed.inset-0.backdrop-blur-sm' + ]; + + let found = false; + for (const selector of modalSelectors) { + const elements = document.querySelectorAll(selector); + if (elements.length > 0) { + found = true; + console.log(`Found modal with selector: ${selector}`); + } + } + + return { + correctOpacityFound: found, + totalModalElements: document.querySelectorAll('.fixed.inset-0').length + }; + }); + + console.log('Final opacity verification:', opacityVerification); + }); +}); \ No newline at end of file diff --git a/test-manage-redirect.cjs b/test-manage-redirect.cjs new file mode 100644 index 0000000..fff50fd --- /dev/null +++ b/test-manage-redirect.cjs @@ -0,0 +1,50 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Event Management Page', () => { + test('should redirect to login with proper redirect parameter when not authenticated', async ({ page }) => { + // Navigate to a management page without being authenticated + await page.goto('/events/7ac12bd2-8509-4db3-b1bc-98a808646311/manage'); + + // Should redirect to login page with redirect parameter + await expect(page).toHaveURL(/\/login-new\?redirect=.*manage/); + + // Verify the redirect parameter contains the original URL + const url = page.url(); + const urlParams = new URLSearchParams(url.split('?')[1]); + const redirectParam = urlParams.get('redirect'); + + expect(redirectParam).toContain('/events/7ac12bd2-8509-4db3-b1bc-98a808646311/manage'); + }); + + test('should redirect back to original page after successful login', async ({ page }) => { + // Go to login page with redirect parameter + await page.goto('/login-new?redirect=%2Fevents%2F7ac12bd2-8509-4db3-b1bc-98a808646311%2Fmanage'); + + // Wait for the login form to load + await page.waitForSelector('#email'); + await page.waitForSelector('#password'); + + // Fill in login credentials (you might need to adjust these) + await page.fill('#email', 'test@example.com'); + await page.fill('#password', 'password123'); + + // Submit the form + await page.click('#login-btn'); + + // Wait for redirect (this might fail if credentials are invalid, but shows intent) + try { + await page.waitForURL(/\/events\/.*\/manage/, { timeout: 5000 }); + console.log('Successfully redirected to manage page'); + } catch (error) { + console.log('Login failed or redirect timeout - this is expected with test credentials'); + } + }); + + test('should show 404 page for invalid event ID', async ({ page }) => { + // Navigate to an invalid event ID + await page.goto('/events/invalid-event-id/manage'); + + // Should still redirect to login first + await expect(page).toHaveURL(/\/login-new\?redirect=.*invalid-event-id.*manage/); + }); +}); \ No newline at end of file diff --git a/test-results/.last-run.json b/test-results/.last-run.json index cbcc1fb..01c1da6 100644 --- a/test-results/.last-run.json +++ b/test-results/.last-run.json @@ -1,4 +1,6 @@ { - "status": "passed", - "failedTests": [] + "status": "failed", + "failedTests": [ + "02f76897f4525b4c9d46-0bfd6441b30ce541a2ca" + ] } \ No newline at end of file diff --git a/test-results/test-dark-mode-modal.cjs-D-7b86c-rk-mode-with-proper-opacity-chromium/error-context.md b/test-results/test-dark-mode-modal.cjs-D-7b86c-rk-mode-with-proper-opacity-chromium/error-context.md new file mode 100644 index 0000000..b045359 --- /dev/null +++ b/test-results/test-dark-mode-modal.cjs-D-7b86c-rk-mode-with-proper-opacity-chromium/error-context.md @@ -0,0 +1,122 @@ +# Page snapshot + +```yaml +- link "Skip to main content": + - /url: "#main-content" +- link "Skip to navigation": + - /url: "#navigation" +- main: + - navigation: + - link "BCT": + - /url: /dashboard + - img + - text: BCT + - link "All Events": + - /url: /dashboard + - img + - text: All Events + - text: "| Event Management" + - button "Switch to dark mode": + - img + - button "T Tyler Admin": + - text: T Tyler Admin + - img + - main: + - heading "Garfield County Fair & Rodeo 2025" [level=1] + - img + - text: Garfield County Fairgrounds - Rifle, CO + - img + - text: Wednesday, August 6, 2025 at 12:00 AM + - paragraph: Secure your spot at the 87-year-strong Garfield County Fair & Rodeo, returning to Rifle, Colorado August 2 and 6-9, 2025... + - button "Show more" + - text: $0 Total Revenue + - link "Preview": + - /url: /e/firebase-event-zmkx63pewurqyhtw6tsn + - img + - text: Preview + - button "Embed": + - img + - text: Embed + - button "Edit": + - img + - text: Edit + - link "Scanner": + - /url: /scan + - img + - text: Scanner + - link "Kiosk": + - /url: /kiosk/firebase-event-zmkx63pewurqyhtw6tsn + - img + - text: Kiosk + - button "PIN": + - img + - text: PIN + - paragraph: Tickets Sold + - paragraph: "0" + - img + - text: +12% from last week + - img + - paragraph: Available + - paragraph: "0" + - img + - text: Ready to sell + - img + - paragraph: Check-ins + - paragraph: "0" + - img + - text: 85% attendance rate + - img + - paragraph: Net Revenue + - paragraph: $0 + - img + - text: +24% this month + - img + - button "Ticketing & Access": + - img + - text: Ticketing & Access + - button "Custom Pages": + - img + - text: Custom Pages + - button "Sales": + - img + - text: Sales + - button "Attendees": + - img + - text: Attendees + - button "Event Settings": + - img + - text: Event Settings + - button "Ticket Types" + - button "Access Codes" + - button "Discounts" + - button "Printed Tickets" + - heading "Ticket Types & Pricing" [level=2] + - button: + - img + - button: + - img + - button "Add Ticket Type": + - img + - text: Add Ticket Type + - img + - paragraph: No ticket types created yet + - button "Create Your First Ticket Type" +- contentinfo: + - link "Terms of Service": + - /url: /terms + - link "Privacy Policy": + - /url: /privacy + - link "Support": + - /url: /support + - text: © 2025 All rights reserved Powered by + - link "blackcanyontickets.com": + - /url: https://blackcanyontickets.com +- img +- heading "Cookie Preferences" [level=3] +- paragraph: + - text: We use essential cookies to make our website work and analytics cookies to understand how you interact with our site. + - link "Learn more in our Privacy Policy": + - /url: /privacy +- button "Manage Preferences" +- button "Accept All" +``` \ No newline at end of file diff --git a/test-results/test-dark-mode-modal.cjs-D-7b86c-rk-mode-with-proper-opacity-chromium/test-failed-1.png b/test-results/test-dark-mode-modal.cjs-D-7b86c-rk-mode-with-proper-opacity-chromium/test-failed-1.png new file mode 100644 index 0000000..4d11bd7 Binary files /dev/null and b/test-results/test-dark-mode-modal.cjs-D-7b86c-rk-mode-with-proper-opacity-chromium/test-failed-1.png differ diff --git a/test-simple-buttons.cjs b/test-simple-buttons.cjs new file mode 100644 index 0000000..f24e0f6 --- /dev/null +++ b/test-simple-buttons.cjs @@ -0,0 +1,109 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Simple Button Test', () => { + test('should test buttons with dev server', async ({ page }) => { + console.log('Testing buttons on dev server...'); + + // Navigate to the manage page that's actually being accessed + await page.goto('http://localhost:3001/events/d89dd7ec-01b3-4b89-a52e-b08afaf7661d/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(3000); // Give time for React components to load + + // Take screenshot + await page.screenshot({ path: 'manage-page-dev.png', fullPage: true }); + console.log('Manage page screenshot saved'); + + const currentUrl = page.url(); + console.log(`Current URL: ${currentUrl}`); + + if (currentUrl.includes('/login')) { + console.log('Still redirected to login - authentication required'); + return; + } + + // Check for our simple embed test component + const simpleTestCheck = await page.evaluate(() => { + return { + hasReact: typeof React !== 'undefined', + hasReactDOM: typeof ReactDOM !== 'undefined', + simpleEmbedFunction: typeof window.openEmbedModal === 'function', + astroIslands: document.querySelectorAll('astro-island').length, + embedButton: !!document.getElementById('embed-code-btn'), + allButtons: document.querySelectorAll('button').length, + buttonTexts: Array.from(document.querySelectorAll('button')).map(b => b.textContent?.trim()).filter(t => t), + consoleMessages: [] + }; + }); + + console.log('Page analysis:', JSON.stringify(simpleTestCheck, null, 2)); + + // Look specifically for the embed button + const embedButton = page.locator('#embed-code-btn'); + const embedExists = await embedButton.count(); + console.log(`Embed button exists: ${embedExists > 0}`); + + if (embedExists > 0) { + console.log('Testing embed button...'); + + // Set up dialog handler for the alert + page.on('dialog', async dialog => { + console.log('Alert dialog appeared:', dialog.message()); + await dialog.accept(); + }); + + // Click the embed button + await embedButton.click(); + await page.waitForTimeout(2000); + + console.log('Embed button clicked'); + + // Check if window.openEmbedModal exists after click + const afterClick = await page.evaluate(() => { + return { + openEmbedModalExists: typeof window.openEmbedModal === 'function', + windowKeys: Object.keys(window).filter(k => k.includes('embed')), + }; + }); + + console.log('After click analysis:', afterClick); + } + + // Look for ticket pricing buttons + const ticketButtons = page.locator('button').filter({ hasText: /Add Ticket|Create.*Ticket/i }); + const ticketButtonCount = await ticketButtons.count(); + console.log(`Ticket pricing buttons found: ${ticketButtonCount}`); + + if (ticketButtonCount > 0) { + console.log('Testing ticket button...'); + await ticketButtons.first().click(); + await page.waitForTimeout(1000); + + // Look for modal or any response + const modalCheck = await page.evaluate(() => { + const modals = document.querySelectorAll('[role="dialog"], .modal, [data-testid*="modal"]'); + return { + modalCount: modals.length, + modalInfo: Array.from(modals).map(m => ({ + id: m.id, + className: m.className, + visible: !m.hidden && m.style.display !== 'none' + })) + }; + }); + + console.log('Modal check after ticket button:', modalCheck); + + await page.screenshot({ path: 'after-ticket-button-dev.png', fullPage: true }); + } + + // Check for any JavaScript errors + const errors = []; + page.on('console', msg => { + if (msg.type() === 'error') { + errors.push(msg.text()); + } + }); + + console.log('JavaScript errors:', errors); + }); +}); \ No newline at end of file diff --git a/test-ticket-auth.cjs b/test-ticket-auth.cjs new file mode 100644 index 0000000..6674e3d --- /dev/null +++ b/test-ticket-auth.cjs @@ -0,0 +1,189 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Ticket Buttons with Authentication', () => { + test('should login and test ticket pricing buttons', async ({ page }) => { + console.log('Testing ticket buttons with authentication...'); + + // First, go to login page + await page.goto('http://localhost:3001/login-new'); + await page.waitForLoadState('networkidle'); + + // Take screenshot of login page + await page.screenshot({ path: 'login-before-auth.png', fullPage: true }); + + // Fill in login credentials + console.log('Filling login form...'); + await page.fill('input[type="email"], input[name="email"]', 'tmartinez@gmail.com'); + await page.fill('input[type="password"], input[name="password"]', 'TestPassword123!'); + + // Submit login form + await page.click('button[type="submit"], button:has-text("Sign In"), button:has-text("Login")'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(3000); + + console.log('Login submitted, current URL:', page.url()); + + // Check if we're logged in successfully + if (page.url().includes('/login')) { + console.log('Still on login page - checking for errors'); + await page.screenshot({ path: 'login-failed.png', fullPage: true }); + + // Look for error messages + const errorMsg = await page.locator('.error, [class*="error"], [role="alert"]').textContent().catch(() => null); + console.log('Login error message:', errorMsg); + + return; // Exit if login failed + } + + console.log('Login successful! Now navigating to manage page...'); + + // Navigate to the manage page + await page.goto('http://localhost:3001/events/d89dd7ec-01b3-4b89-a52e-b08afaf7661d/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(5000); // Wait for React components to load + + await page.screenshot({ path: 'manage-page-authenticated.png', fullPage: true }); + + console.log('Manage page loaded, current URL:', page.url()); + + // Click on Ticket Types tab specifically + const ticketTypesTab = page.locator('button').filter({ hasText: /Ticket Types/i }); + const ticketTypesCount = await ticketTypesTab.count(); + console.log(`Ticket Types tab found: ${ticketTypesCount > 0}`); + + if (ticketTypesCount > 0) { + console.log('Clicking Ticket Types tab...'); + await ticketTypesTab.first().click(); + await page.waitForTimeout(5000); // Wait longer for React component to load + await page.screenshot({ path: 'ticket-types-tab-active.png', fullPage: true }); + + // Wait for any loading states to complete + await page.waitForFunction(() => { + // Wait for either a loading indicator to disappear or ticket content to appear + return !document.querySelector('[data-loading="true"]') || + document.querySelector('[class*="ticket"]') || + document.querySelector('button[contains(., "Add Ticket")]'); + }, { timeout: 10000 }).catch(() => { + console.log('Timeout waiting for ticket content to load'); + }); + + await page.screenshot({ path: 'after-ticket-types-load.png', fullPage: true }); + } + + // Now look for ticket pricing buttons + console.log('Looking for ticket pricing buttons...'); + + // Get all buttons and their text + const allButtons = await page.locator('button').all(); + const buttonInfo = []; + for (const button of allButtons) { + const text = await button.textContent(); + const isVisible = await button.isVisible(); + const isEnabled = await button.isEnabled(); + if (text && text.trim()) { + buttonInfo.push({ + text: text.trim(), + visible: isVisible, + enabled: isEnabled + }); + } + } + + console.log('All buttons found:', buttonInfo); + + // Look specifically for ticket buttons + const addTicketButton = page.locator('button').filter({ hasText: /Add Ticket Type/i }); + const createFirstButton = page.locator('button').filter({ hasText: /Create.*First.*Ticket/i }); + + const addTicketCount = await addTicketButton.count(); + const createFirstCount = await createFirstButton.count(); + + console.log(`"Add Ticket Type" buttons: ${addTicketCount}`); + console.log(`"Create Your First Ticket Type" buttons: ${createFirstCount}`); + + // Test the Add Ticket Type button + if (addTicketCount > 0) { + console.log('Testing "Add Ticket Type" button...'); + + // Check if button is visible and enabled + const isVisible = await addTicketButton.first().isVisible(); + const isEnabled = await addTicketButton.first().isEnabled(); + console.log(`Add Ticket button - Visible: ${isVisible}, Enabled: ${isEnabled}`); + + if (isVisible && isEnabled) { + // Set up console message monitoring for ALL console messages + const consoleMessages = []; + page.on('console', msg => { + consoleMessages.push(`${msg.type()}: ${msg.text()}`); + console.log(`CONSOLE ${msg.type()}: ${msg.text()}`); + }); + + // Click the button + await addTicketButton.first().click(); + await page.waitForTimeout(2000); + + // Check for modal + const modalCheck = await page.evaluate(() => { + const modals = document.querySelectorAll('[role="dialog"], .modal, [class*="Modal"], [data-testid*="modal"]'); + const visibleModals = Array.from(modals).filter(m => { + const style = window.getComputedStyle(m); + return style.display !== 'none' && style.visibility !== 'hidden' && m.offsetParent !== null; + }); + + return { + totalModals: modals.length, + visibleModals: visibleModals.length, + modalElements: Array.from(modals).map(m => ({ + tagName: m.tagName, + id: m.id, + className: m.className, + isVisible: m.offsetParent !== null + })) + }; + }); + + console.log('Modal check after Add Ticket Type click:', modalCheck); + console.log('Console messages:', consoleMessages); + + await page.screenshot({ path: 'after-add-ticket-click-authenticated.png', fullPage: true }); + } + } + + // Test the Create Your First Ticket Type button + if (createFirstCount > 0) { + console.log('Testing "Create Your First Ticket Type" button...'); + + const isVisible = await createFirstButton.first().isVisible(); + const isEnabled = await createFirstButton.first().isEnabled(); + console.log(`Create First Ticket button - Visible: ${isVisible}, Enabled: ${isEnabled}`); + + if (isVisible && isEnabled) { + await createFirstButton.first().click(); + await page.waitForTimeout(2000); + + const modalCheck = await page.evaluate(() => { + const modals = document.querySelectorAll('[role="dialog"], .modal, [class*="Modal"], [data-testid*="modal"]'); + return { + totalModals: modals.length, + visibleModals: Array.from(modals).filter(m => m.offsetParent !== null).length + }; + }); + + console.log('Modal check after Create First Ticket click:', modalCheck); + await page.screenshot({ path: 'after-create-first-authenticated.png', fullPage: true }); + } + } + + // Check if the TicketTypeModal component is actually loaded + const componentCheck = await page.evaluate(() => { + return { + astroIslands: document.querySelectorAll('astro-island').length, + ticketComponents: document.querySelectorAll('[class*="Ticket"]').length, + reactComponents: document.querySelectorAll('[data-reactroot]').length, + ticketTypeModalElements: document.querySelectorAll('[class*="TicketTypeModal"], [id*="ticket-modal"]').length + }; + }); + + console.log('Component analysis:', componentCheck); + }); +}); \ No newline at end of file diff --git a/test-ticket-buttons.cjs b/test-ticket-buttons.cjs new file mode 100644 index 0000000..b56ee38 --- /dev/null +++ b/test-ticket-buttons.cjs @@ -0,0 +1,173 @@ +const { test, expect } = require('@playwright/test'); + +test.describe('Ticket Pricing Buttons Test', () => { + test('should test ticket pricing buttons functionality', async ({ page }) => { + console.log('Testing ticket pricing buttons...'); + + // Try to navigate to a manage page + await page.goto('http://localhost:3001/events/d89dd7ec-01b3-4b89-a52e-b08afaf7661d/manage'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + const currentUrl = page.url(); + console.log(`Current URL: ${currentUrl}`); + + if (currentUrl.includes('/login')) { + console.log('Authentication required - trying to login first or bypass'); + + // Let's try to access the home page and see if we can find any accessible pages + await page.goto('http://localhost:3001/'); + await page.waitForLoadState('networkidle'); + + // Take screenshot of homepage + await page.screenshot({ path: 'homepage-access.png', fullPage: true }); + + // Try to find any manage links or test events + const links = await page.locator('a[href*="/events/"]').count(); + console.log(`Found ${links} event links on homepage`); + + if (links > 0) { + const eventLinks = await page.locator('a[href*="/events/"]').all(); + for (let i = 0; i < Math.min(3, eventLinks.length); i++) { + const href = await eventLinks[i].getAttribute('href'); + console.log(`Found event link: ${href}`); + } + } + + return; // Skip the rest if we can't access manage page + } + + // If we made it here, we have access to the manage page + await page.screenshot({ path: 'manage-page-accessible.png', fullPage: true }); + + // Wait for React components to load + await page.waitForTimeout(5000); + + // Check for ticket-related buttons specifically + console.log('Looking for ticket pricing buttons...'); + + // Check if the tickets tab is visible/active + const ticketsTab = page.locator('button, [role="tab"]').filter({ hasText: /tickets/i }); + const ticketsTabCount = await ticketsTab.count(); + console.log(`Tickets tab found: ${ticketsTabCount > 0}`); + + if (ticketsTabCount > 0) { + console.log('Clicking tickets tab...'); + await ticketsTab.first().click(); + await page.waitForTimeout(2000); + } + + // Look for "Add Ticket Type" button + const addTicketButton = page.locator('button').filter({ hasText: /Add Ticket Type/i }); + const addTicketCount = await addTicketButton.count(); + console.log(`"Add Ticket Type" buttons found: ${addTicketCount}`); + + // Look for "Create Your First Ticket Type" button + const createFirstTicketButton = page.locator('button').filter({ hasText: /Create.*First.*Ticket/i }); + const createFirstCount = await createFirstTicketButton.count(); + console.log(`"Create Your First Ticket Type" buttons found: ${createFirstCount}`); + + // Get all button texts to see what's available + const allButtons = await page.locator('button').all(); + const buttonTexts = []; + for (const button of allButtons) { + const text = await button.textContent(); + if (text && text.trim()) { + buttonTexts.push(text.trim()); + } + } + console.log('All button texts found:', buttonTexts); + + // Check for React component mounting + const reactInfo = await page.evaluate(() => { + return { + astroIslands: document.querySelectorAll('astro-island').length, + reactElements: document.querySelectorAll('[data-reactroot], [data-react-class]').length, + ticketComponents: document.querySelectorAll('[class*="Ticket"], [id*="ticket"]').length, + modalElements: document.querySelectorAll('[role="dialog"], .modal, [data-testid*="modal"]').length, + hasWindowOpenEmbedModal: typeof window.openEmbedModal === 'function' + }; + }); + + console.log('React component info:', reactInfo); + + // Test the "Add Ticket Type" button if it exists + if (addTicketCount > 0) { + console.log('Testing "Add Ticket Type" button...'); + + // Set up console monitoring + const consoleMessages = []; + page.on('console', msg => { + consoleMessages.push(`${msg.type()}: ${msg.text()}`); + }); + + await addTicketButton.first().click(); + await page.waitForTimeout(2000); + + // Check for modal after click + const modalAfterAdd = await page.evaluate(() => { + const modals = document.querySelectorAll('[role="dialog"], .modal, [class*="Modal"]'); + return { + modalCount: modals.length, + modalsVisible: Array.from(modals).filter(m => + !m.hidden && + m.style.display !== 'none' && + m.offsetParent !== null + ).length + }; + }); + + console.log('Modal status after "Add Ticket Type" click:', modalAfterAdd); + console.log('Console messages during click:', consoleMessages); + + await page.screenshot({ path: 'after-add-ticket-click.png', fullPage: true }); + } + + // Test the "Create Your First Ticket Type" button if it exists + if (createFirstCount > 0) { + console.log('Testing "Create Your First Ticket Type" button...'); + + await createFirstTicketButton.first().click(); + await page.waitForTimeout(2000); + + const modalAfterCreate = await page.evaluate(() => { + const modals = document.querySelectorAll('[role="dialog"], .modal, [class*="Modal"]'); + return { + modalCount: modals.length, + modalsVisible: Array.from(modals).filter(m => + !m.hidden && + m.style.display !== 'none' && + m.offsetParent !== null + ).length + }; + }); + + console.log('Modal status after "Create Your First Ticket Type" click:', modalAfterCreate); + + await page.screenshot({ path: 'after-create-first-ticket-click.png', fullPage: true }); + } + + // Check for any JavaScript errors + const pageErrors = []; + page.on('pageerror', error => { + pageErrors.push(error.message); + }); + + console.log('Page errors found:', pageErrors); + + // Final check - try to find any ticket-related React components + const ticketComponentCheck = await page.evaluate(() => { + // Look for specific ticket component indicators + const indicators = { + ticketTabsComponent: !!document.querySelector('[class*="TicketsTab"]'), + ticketTypeModal: !!document.querySelector('[class*="TicketTypeModal"]'), + ticketManagement: !!document.querySelector('[class*="ticket"], [class*="Ticket"]'), + hasTicketTypes: !!document.querySelector('[data-testid*="ticket"], [id*="ticket"]') + }; + + return indicators; + }); + + console.log('Ticket component indicators:', ticketComponentCheck); + }); +}); \ No newline at end of file diff --git a/ticket-types-tab-active.png b/ticket-types-tab-active.png new file mode 100644 index 0000000..2094bdd Binary files /dev/null and b/ticket-types-tab-active.png differ diff --git a/tickets-tab-active.png b/tickets-tab-active.png new file mode 100644 index 0000000..ab11938 Binary files /dev/null and b/tickets-tab-active.png differ