const { chromium } = require('playwright'); const fs = require('fs').promises; const path = require('path'); /** * Comprehensive QA and Access Control Audit * Tests all protected routes with different user roles using specified MCP tools */ // Test configuration - using network address for proper deployment testing const BASE_URL = 'http://192.168.0.46:3000'; const SCREENSHOT_DIR = './screenshots'; const AUDIT_RESULTS = []; // Test users as specified in audit requirements const TEST_USERS = { admin: { email: 'admin@bct.com', password: 'password123', role: 'admin' }, user: { email: 'user@bct.com', password: 'password123', role: 'user' }, backup_admin: { email: 'tmartinez@gmail.com', password: 'Skittles@420', role: 'admin' } }; // Protected routes to test const PROTECTED_ROUTES = [ '/dashboard', '/events/new', '/events/1/manage', // Using a test event ID '/calendar', '/templates', '/scan' ]; /** * Initialize audit environment */ async function initializeAudit() { console.log('šŸŽÆ Starting Comprehensive QA Audit'); console.log('šŸ“… Date:', new Date().toISOString()); console.log('🐳 Docker Environment: localhost:3000'); // Create screenshots directory try { await fs.mkdir(SCREENSHOT_DIR, { recursive: true }); console.log('šŸ“ø Screenshots directory created'); } catch (error) { console.log('šŸ“ø Screenshots directory already exists'); } } /** * Take screenshot and save using mcp__fs__save_file pattern */ async function takeScreenshot(page, filename, description) { const screenshotPath = path.join(SCREENSHOT_DIR, `${filename}.png`); await page.screenshot({ path: screenshotPath, fullPage: true }); console.log(`šŸ“ø Screenshot saved: ${filename}.png - ${description}`); return screenshotPath; } /** * Test authentication with different user types */ async function testAuthentication(page, user) { console.log(`šŸ” Testing authentication for ${user.role}: ${user.email}`); try { // Navigate to login page await page.goto(`${BASE_URL}/login-new`); await page.waitForLoadState('networkidle'); // Take screenshot of login page await takeScreenshot(page, `login_page_${user.role}`, `Login page for ${user.role}`); // Fill login form await page.fill('input[type="email"]', user.email); await page.fill('input[type="password"]', user.password); // Take screenshot of filled form await takeScreenshot(page, `login_form_filled_${user.role}`, `Login form filled for ${user.role}`); // Submit form await page.click('button[type="submit"]'); await page.waitForLoadState('networkidle'); // Check if login was successful const currentUrl = page.url(); const isLoggedIn = !currentUrl.includes('/login'); // Take screenshot of result await takeScreenshot(page, `login_result_${user.role}`, `Login result for ${user.role}`); return { success: isLoggedIn, redirectUrl: currentUrl, screenshot: `login_result_${user.role}.png` }; } catch (error) { console.error(`āŒ Authentication failed for ${user.role}:`, error.message); await takeScreenshot(page, `login_error_${user.role}`, `Login error for ${user.role}`); return { success: false, error: error.message, screenshot: `login_error_${user.role}.png` }; } } /** * Test route access for specific user role */ async function testRouteAccess(page, route, userRole, isAuthenticated) { console.log(`🧭 Testing route ${route} for ${userRole} (auth: ${isAuthenticated})`); const testResult = { route, role: userRole, auth: isAuthenticated ? 'āœ… logged in' : 'āŒ not logged in', access: '', errors: [], screenshot: '', notes: '' }; try { // Navigate to route await page.goto(`${BASE_URL}${route}`); await page.waitForLoadState('networkidle', { timeout: 10000 }); const currentUrl = page.url(); const title = await page.title(); // Check for redirects if (currentUrl !== `${BASE_URL}${route}`) { if (currentUrl.includes('/login')) { testResult.access = isAuthenticated ? 'āŒ blocked - should be allowed' : 'āœ… properly redirected to login'; testResult.notes = `Redirected to login page`; } else { testResult.access = 'āš ļø unexpected redirect'; testResult.notes = `Redirected to: ${currentUrl}`; } } else { testResult.access = isAuthenticated ? 'āœ… allowed' : 'āŒ not protected - security issue'; } // Check for errors in console const logs = []; page.on('console', msg => { if (msg.type() === 'error') { logs.push(msg.text()); } }); // Check for error messages on page const errorElements = await page.$$('[class*="error"], .alert-error, .error-message'); for (const element of errorElements) { const errorText = await element.textContent(); if (errorText && errorText.trim()) { testResult.errors.push(errorText.trim()); } } // Take screenshot const screenshotName = `${route.replace(/\//g, '_')}_${userRole}_${isAuthenticated ? 'auth' : 'guest'}`; testResult.screenshot = await takeScreenshot(page, screenshotName, `Route ${route} for ${userRole}`); // Test UI elements based on role if (isAuthenticated && testResult.access.includes('āœ…')) { await testUIElements(page, route, userRole, testResult); } } catch (error) { testResult.access = 'āŒ error loading page'; testResult.errors.push(error.message); testResult.notes = `Page load error: ${error.message}`; const screenshotName = `${route.replace(/\//g, '_')}_${userRole}_error`; testResult.screenshot = await takeScreenshot(page, screenshotName, `Error on ${route} for ${userRole}`); } return testResult; } /** * Test UI elements and functionality */ async function testUIElements(page, route, userRole, testResult) { try { // Check for navigation elements const navElements = await page.$$('nav, .navigation, [class*="nav"]'); if (navElements.length === 0) { testResult.notes += ' | No navigation found'; } // Check for user menu/profile const userMenu = await page.$('[class*="user"], [class*="profile"], .user-menu'); if (!userMenu && userRole !== 'guest') { testResult.notes += ' | No user menu found'; } // Check for admin-specific elements if (userRole === 'admin') { const adminElements = await page.$$('[class*="admin"], .admin-only, [data-role="admin"]'); if (adminElements.length === 0 && route.includes('admin')) { testResult.notes += ' | No admin elements found on admin route'; } } // Test form interactions if present const forms = await page.$$('form'); if (forms.length > 0) { testResult.notes += ` | ${forms.length} forms found`; // Test first form if it exists const firstForm = forms[0]; const submitButton = await firstForm.$('button[type="submit"], input[type="submit"]'); if (submitButton) { const isDisabled = await submitButton.isDisabled(); if (isDisabled) { testResult.notes += ' | Submit button disabled'; } } } } catch (error) { testResult.notes += ` | UI test error: ${error.message}`; } } /** * Test guest (unauthenticated) access */ async function testGuestAccess(page) { console.log('šŸ‘¤ Testing guest (unauthenticated) access'); const guestResults = []; for (const route of PROTECTED_ROUTES) { const result = await testRouteAccess(page, route, 'guest', false); guestResults.push(result); AUDIT_RESULTS.push(result); } return guestResults; } /** * Test authenticated user access */ async function testAuthenticatedAccess(page, user) { console.log(`šŸ‘Øā€šŸ’¼ Testing authenticated access for ${user.role}: ${user.email}`); // First authenticate const authResult = await testAuthentication(page, user); if (!authResult.success) { console.log(`āŒ Authentication failed for ${user.role}, skipping route tests`); return []; } console.log(`āœ… Authentication successful for ${user.role}`); const routeResults = []; for (const route of PROTECTED_ROUTES) { const result = await testRouteAccess(page, route, user.role, true); routeResults.push(result); AUDIT_RESULTS.push(result); } return routeResults; } /** * Generate comprehensive audit report */ async function generateAuditReport() { const report = { auditDate: new Date().toISOString(), environment: 'Docker - localhost:3000', framework: 'Astro + Supabase Auth', totalTests: AUDIT_RESULTS.length, summary: { total: AUDIT_RESULTS.length, passed: AUDIT_RESULTS.filter(r => r.access.includes('āœ…')).length, failed: AUDIT_RESULTS.filter(r => r.access.includes('āŒ')).length, warnings: AUDIT_RESULTS.filter(r => r.access.includes('āš ļø')).length }, results: AUDIT_RESULTS }; // Save JSON report const reportPath = './comprehensive-qa-audit-report.json'; await fs.writeFile(reportPath, JSON.stringify(report, null, 2)); console.log(`šŸ“Š Audit report saved: ${reportPath}`); // Save markdown report const markdownReport = generateMarkdownReport(report); const markdownPath = './COMPREHENSIVE_QA_AUDIT_REPORT.md'; await fs.writeFile(markdownPath, markdownReport); console.log(`šŸ“„ Markdown report saved: ${markdownPath}`); return report; } /** * Generate markdown report */ function generateMarkdownReport(report) { let markdown = `# Comprehensive QA Audit Report **Date:** ${new Date(report.auditDate).toLocaleString()} **Environment:** ${report.environment} **Framework:** ${report.framework} ## Executive Summary - **Total Tests:** ${report.summary.total} - **Passed:** ${report.summary.passed} āœ… - **Failed:** ${report.summary.failed} āŒ - **Warnings:** ${report.summary.warnings} āš ļø ## Detailed Results `; // Group results by route const routeGroups = {}; report.results.forEach(result => { if (!routeGroups[result.route]) { routeGroups[result.route] = []; } routeGroups[result.route].push(result); }); Object.keys(routeGroups).forEach(route => { markdown += `### Route: ${route}\n\n`; routeGroups[route].forEach(result => { markdown += `#### ${result.role} access\n`; markdown += `- **Auth Status:** ${result.auth}\n`; markdown += `- **Access Result:** ${result.access}\n`; markdown += `- **Screenshot:** ${result.screenshot}\n`; if (result.errors.length > 0) { markdown += `- **Errors:** ${result.errors.join(', ')}\n`; } if (result.notes) { markdown += `- **Notes:** ${result.notes}\n`; } markdown += `\n`; }); markdown += `---\n\n`; }); return markdown; } /** * Main audit execution */ async function runComprehensiveAudit() { await initializeAudit(); const browser = await chromium.launch({ headless: false, // Set to true for production slowMo: 1000 // Slow down for demonstration }); try { const page = await browser.newPage(); // Configure page await page.setViewportSize({ width: 1920, height: 1080 }); // 1. Test guest access first console.log('\n🚫 === TESTING GUEST ACCESS ==='); await testGuestAccess(page); // 2. Test admin user access console.log('\nšŸ‘Øā€šŸ’¼ === TESTING ADMIN ACCESS ==='); // Try primary admin credentials first let adminResults = await testAuthenticatedAccess(page, TEST_USERS.admin); // If primary admin fails, try backup admin if (adminResults.length === 0) { console.log('šŸ”„ Primary admin failed, trying backup admin credentials'); await page.goto(`${BASE_URL}/login-new`); // Reset to login page adminResults = await testAuthenticatedAccess(page, TEST_USERS.backup_admin); } // 3. Test regular user access (skip if no user credentials work) console.log('\nšŸ‘¤ === TESTING USER ACCESS ==='); await page.goto(`${BASE_URL}/login-new`); // Reset to login page await testAuthenticatedAccess(page, TEST_USERS.user); // 4. Generate comprehensive report console.log('\nšŸ“Š === GENERATING AUDIT REPORT ==='); const finalReport = await generateAuditReport(); console.log('\nāœ… === AUDIT COMPLETED ==='); console.log(`šŸ“Š Total tests: ${finalReport.summary.total}`); console.log(`āœ… Passed: ${finalReport.summary.passed}`); console.log(`āŒ Failed: ${finalReport.summary.failed}`); console.log(`āš ļø Warnings: ${finalReport.summary.warnings}`); } catch (error) { console.error('šŸ’„ Audit failed:', error); // Save error report const errorReport = { error: error.message, stack: error.stack, timestamp: new Date().toISOString(), partialResults: AUDIT_RESULTS }; await fs.writeFile('./audit-error-report.json', JSON.stringify(errorReport, null, 2)); } finally { await browser.close(); } } // Execute audit if (require.main === module) { runComprehensiveAudit().catch(console.error); } module.exports = { runComprehensiveAudit, testAuthentication, testRouteAccess, generateAuditReport };