Major fixes and improvements: - Fixed edit event button functionality with proper event handlers and DOM ready state checking - Added status column to tickets table via Supabase migration to resolve 500 API errors - Updated stats API to correctly calculate revenue from decimal price values - Resolved authentication redirect loops by fixing cookie configuration for Docker environment - Fixed Permissions-Policy header syntax errors - Added comprehensive debugging and error handling for event management - Implemented modal-based event editing with form validation and API integration - Enhanced event data loading with proper error handling and user feedback 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
324 lines
14 KiB
JavaScript
324 lines
14 KiB
JavaScript
const { chromium } = require('playwright');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
async function runQATests() {
|
||
const browser = await chromium.launch({ headless: false });
|
||
const context = await browser.newContext({
|
||
viewport: { width: 1280, height: 720 }
|
||
});
|
||
const page = await context.newPage();
|
||
|
||
// Monitor console errors
|
||
const consoleErrors = [];
|
||
page.on('console', msg => {
|
||
if (msg.type() === 'error') {
|
||
consoleErrors.push({
|
||
timestamp: new Date().toISOString(),
|
||
message: msg.text(),
|
||
url: page.url()
|
||
});
|
||
}
|
||
});
|
||
|
||
const results = {
|
||
themeTests: {},
|
||
interactiveTests: {},
|
||
mobileTests: {},
|
||
consoleErrors: []
|
||
};
|
||
|
||
try {
|
||
console.log('🚀 Starting QA Tests - Theme Functionality and Interactive Components');
|
||
|
||
// Navigate to login page
|
||
console.log('\n📋 Step 1: Logging in...');
|
||
await page.goto('http://localhost:3001/login');
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// Take screenshot of login page
|
||
await page.screenshot({ path: 'login-page-qa.png', fullPage: true });
|
||
|
||
// Login
|
||
await page.fill('input[type="email"]', 'tmartinez@gmail.com');
|
||
await page.fill('input[type="password"]', 'Skittles@420');
|
||
await page.click('button[type="submit"]');
|
||
|
||
// Wait for dashboard
|
||
await page.waitForURL('**/dashboard', { timeout: 10000 });
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
console.log('✅ Successfully logged in');
|
||
|
||
// THEME TESTING
|
||
console.log('\n🎨 Step 2: Testing Theme Functionality...');
|
||
results.themeTests.startingTheme = 'Testing for theme toggle elements';
|
||
|
||
// Look for theme toggle elements
|
||
const themeToggleSelectors = [
|
||
'[data-theme-toggle]',
|
||
'.theme-toggle',
|
||
'.dark-mode-toggle',
|
||
'button[aria-label*="theme"]',
|
||
'button[aria-label*="dark"]',
|
||
'button[aria-label*="light"]',
|
||
'[role="switch"]'
|
||
];
|
||
|
||
let themeToggleFound = false;
|
||
let themeToggleElement = null;
|
||
|
||
for (const selector of themeToggleSelectors) {
|
||
try {
|
||
const element = await page.$(selector);
|
||
if (element) {
|
||
themeToggleFound = true;
|
||
themeToggleElement = element;
|
||
results.themeTests.toggleSelector = selector;
|
||
console.log(`✅ Found theme toggle: ${selector}`);
|
||
break;
|
||
}
|
||
} catch (e) {
|
||
// Continue checking other selectors
|
||
}
|
||
}
|
||
|
||
if (!themeToggleFound) {
|
||
// Check navigation area for any button that might be a theme toggle
|
||
const navButtons = await page.$$('nav button, header button');
|
||
console.log(`🔍 Checking ${navButtons.length} navigation buttons for theme toggle...`);
|
||
|
||
for (let i = 0; i < navButtons.length; i++) {
|
||
const button = navButtons[i];
|
||
const text = await button.textContent();
|
||
const ariaLabel = await button.getAttribute('aria-label');
|
||
console.log(`Button ${i}: text="${text}", aria-label="${ariaLabel}"`);
|
||
|
||
if (text && (text.toLowerCase().includes('theme') || text.toLowerCase().includes('dark') || text.toLowerCase().includes('light'))) {
|
||
themeToggleFound = true;
|
||
themeToggleElement = button;
|
||
results.themeTests.toggleSelector = `nav button:nth-child(${i+1})`;
|
||
break;
|
||
}
|
||
if (ariaLabel && (ariaLabel.toLowerCase().includes('theme') || ariaLabel.toLowerCase().includes('dark') || ariaLabel.toLowerCase().includes('light'))) {
|
||
themeToggleFound = true;
|
||
themeToggleElement = button;
|
||
results.themeTests.toggleSelector = `button[aria-label="${ariaLabel}"]`;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (themeToggleFound && themeToggleElement) {
|
||
console.log('🎨 Testing theme toggle functionality...');
|
||
|
||
// Take screenshot before toggle
|
||
await page.screenshot({ path: 'theme-before-toggle.png', fullPage: true });
|
||
results.themeTests.beforeToggleScreenshot = 'theme-before-toggle.png';
|
||
|
||
// Get initial theme state
|
||
const initialTheme = await page.evaluate(() => {
|
||
return {
|
||
documentClass: document.documentElement.className,
|
||
bodyClass: document.body.className,
|
||
localStorage: localStorage.getItem('theme') || localStorage.getItem('dark-mode') || localStorage.getItem('color-theme'),
|
||
dataTheme: document.documentElement.getAttribute('data-theme')
|
||
};
|
||
});
|
||
results.themeTests.initialState = initialTheme;
|
||
|
||
// Click theme toggle
|
||
await themeToggleElement.click();
|
||
await page.waitForTimeout(500); // Wait for theme transition
|
||
|
||
// Get state after toggle
|
||
const afterToggleTheme = await page.evaluate(() => {
|
||
return {
|
||
documentClass: document.documentElement.className,
|
||
bodyClass: document.body.className,
|
||
localStorage: localStorage.getItem('theme') || localStorage.getItem('dark-mode') || localStorage.getItem('color-theme'),
|
||
dataTheme: document.documentElement.getAttribute('data-theme')
|
||
};
|
||
});
|
||
results.themeTests.afterToggleState = afterToggleTheme;
|
||
|
||
// Take screenshot after toggle
|
||
await page.screenshot({ path: 'theme-after-toggle.png', fullPage: true });
|
||
results.themeTests.afterToggleScreenshot = 'theme-after-toggle.png';
|
||
|
||
// Test persistence - reload page
|
||
await page.reload();
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
const afterReloadTheme = await page.evaluate(() => {
|
||
return {
|
||
documentClass: document.documentElement.className,
|
||
bodyClass: document.body.className,
|
||
localStorage: localStorage.getItem('theme') || localStorage.getItem('dark-mode') || localStorage.getItem('color-theme'),
|
||
dataTheme: document.documentElement.getAttribute('data-theme')
|
||
};
|
||
});
|
||
results.themeTests.afterReloadState = afterReloadTheme;
|
||
results.themeTests.persistenceWorks = JSON.stringify(afterToggleTheme) === JSON.stringify(afterReloadTheme);
|
||
|
||
console.log(`✅ Theme toggle found and tested. Persistence: ${results.themeTests.persistenceWorks ? '✅' : '❌'}`);
|
||
} else {
|
||
console.log('❌ No theme toggle found');
|
||
results.themeTests.status = 'No theme toggle functionality found';
|
||
}
|
||
|
||
// INTERACTIVE COMPONENTS TESTING
|
||
console.log('\n🖱️ Step 3: Testing Interactive Components...');
|
||
|
||
// Test navigation menu
|
||
console.log('Testing navigation menu...');
|
||
const navLinks = await page.$$('nav a, header a');
|
||
results.interactiveTests.navigationLinks = navLinks.length;
|
||
console.log(`Found ${navLinks.length} navigation links`);
|
||
|
||
// Test event creation form
|
||
console.log('Testing event creation form...');
|
||
try {
|
||
await page.goto('http://localhost:3001/events/new');
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// Take screenshot of form
|
||
await page.screenshot({ path: 'event-creation-form.png', fullPage: true });
|
||
|
||
// Test form fields
|
||
const formFields = await page.$$('input, textarea, select');
|
||
results.interactiveTests.eventFormFields = formFields.length;
|
||
console.log(`Found ${formFields.length} form fields`);
|
||
|
||
// Test form validation - submit empty form
|
||
console.log('Testing form validation...');
|
||
const submitButton = await page.$('button[type="submit"], input[type="submit"]');
|
||
if (submitButton) {
|
||
await submitButton.click();
|
||
await page.waitForTimeout(1000);
|
||
|
||
// Check for validation messages
|
||
const validationMessages = await page.$$('.error, .invalid, [role="alert"], .text-red-500, .text-red-600');
|
||
results.interactiveTests.validationMessages = validationMessages.length;
|
||
console.log(`Found ${validationMessages.length} validation messages`);
|
||
|
||
// Take screenshot with validation
|
||
await page.screenshot({ path: 'form-validation-test.png', fullPage: true });
|
||
}
|
||
|
||
} catch (e) {
|
||
console.log('❌ Error testing event creation form:', e.message);
|
||
results.interactiveTests.eventFormError = e.message;
|
||
}
|
||
|
||
// Test dashboard interactive elements
|
||
console.log('Testing dashboard interactions...');
|
||
await page.goto('http://localhost:3001/dashboard');
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// Look for buttons, links, and interactive elements
|
||
const buttons = await page.$$('button');
|
||
const links = await page.$$('a');
|
||
const inputs = await page.$$('input, select, textarea');
|
||
|
||
results.interactiveTests.dashboardButtons = buttons.length;
|
||
results.interactiveTests.dashboardLinks = links.length;
|
||
results.interactiveTests.dashboardInputs = inputs.length;
|
||
|
||
console.log(`Dashboard: ${buttons.length} buttons, ${links.length} links, ${inputs.length} inputs`);
|
||
|
||
// Look for modals or dropdowns
|
||
console.log('Looking for modals and dropdowns...');
|
||
const modalTriggers = await page.$$('[data-modal], [data-toggle="modal"], .modal-trigger');
|
||
const dropdownTriggers = await page.$$('[data-dropdown], .dropdown-toggle, [aria-haspopup]');
|
||
|
||
results.interactiveTests.modalTriggers = modalTriggers.length;
|
||
results.interactiveTests.dropdownTriggers = dropdownTriggers.length;
|
||
|
||
// Test any dropdown that exists
|
||
if (dropdownTriggers.length > 0) {
|
||
console.log('Testing dropdown functionality...');
|
||
try {
|
||
await dropdownTriggers[0].click();
|
||
await page.waitForTimeout(500);
|
||
|
||
const dropdownMenu = await page.$('.dropdown-menu, [role="menu"], .dropdown-content');
|
||
results.interactiveTests.dropdownWorks = !!dropdownMenu;
|
||
|
||
if (dropdownMenu) {
|
||
await page.screenshot({ path: 'dropdown-open.png', fullPage: true });
|
||
}
|
||
} catch (e) {
|
||
results.interactiveTests.dropdownError = e.message;
|
||
}
|
||
}
|
||
|
||
// MOBILE RESPONSIVENESS TESTING
|
||
console.log('\n📱 Step 4: Testing Mobile Responsiveness...');
|
||
|
||
// Switch to mobile viewport
|
||
await page.setViewportSize({ width: 375, height: 667 });
|
||
await page.reload();
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// Test mobile navigation
|
||
await page.screenshot({ path: 'mobile-dashboard.png', fullPage: true });
|
||
|
||
// Look for mobile menu toggle
|
||
const mobileMenuToggle = await page.$('.mobile-menu-toggle, .hamburger, [aria-label*="menu"]');
|
||
if (mobileMenuToggle) {
|
||
console.log('Testing mobile menu...');
|
||
await mobileMenuToggle.click();
|
||
await page.waitForTimeout(500);
|
||
await page.screenshot({ path: 'mobile-menu-open.png', fullPage: true });
|
||
results.mobileTests.mobileMenuWorks = true;
|
||
} else {
|
||
results.mobileTests.mobileMenuWorks = false;
|
||
}
|
||
|
||
// Test mobile form
|
||
await page.goto('http://localhost:3001/events/new');
|
||
await page.waitForLoadState('networkidle');
|
||
await page.screenshot({ path: 'mobile-form.png', fullPage: true });
|
||
|
||
// Check for horizontal scroll
|
||
const hasHorizontalScroll = await page.evaluate(() => {
|
||
return document.body.scrollWidth > window.innerWidth;
|
||
});
|
||
results.mobileTests.hasHorizontalScroll = hasHorizontalScroll;
|
||
|
||
console.log(`Mobile menu: ${results.mobileTests.mobileMenuWorks ? '✅' : '❌'}`);
|
||
console.log(`Horizontal scroll: ${hasHorizontalScroll ? '❌' : '✅'}`);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Test execution error:', error);
|
||
results.error = error.message;
|
||
} finally {
|
||
// Collect final console errors
|
||
results.consoleErrors = consoleErrors;
|
||
|
||
// Write results to file
|
||
fs.writeFileSync('qa-test-results.json', JSON.stringify(results, null, 2));
|
||
|
||
console.log('\n📊 QA Test Summary:');
|
||
console.log('Theme Tests:', Object.keys(results.themeTests).length, 'checks');
|
||
console.log('Interactive Tests:', Object.keys(results.interactiveTests).length, 'checks');
|
||
console.log('Mobile Tests:', Object.keys(results.mobileTests).length, 'checks');
|
||
console.log('Console Errors:', consoleErrors.length);
|
||
|
||
if (consoleErrors.length > 0) {
|
||
console.log('\n❌ Console Errors Found:');
|
||
consoleErrors.forEach((error, i) => {
|
||
console.log(`${i+1}. ${error.message} (${error.url})`);
|
||
});
|
||
}
|
||
|
||
console.log('\n📄 Results saved to qa-test-results.json');
|
||
console.log('🖼️ Screenshots saved with descriptive names');
|
||
|
||
await browser.close();
|
||
}
|
||
}
|
||
|
||
// Run the tests
|
||
runQATests().catch(console.error); |