import puppeteer from 'puppeteer'; const targetUrl = process.argv[2] || process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000'; const gatekeeperPassword = process.env.GATEKEEPER_PASSWORD || 'klz2026'; async function run() { console.log(`🚀 Smoke Test: ${targetUrl}`); const browser = await puppeteer.launch({ headless: true, executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-gpu', '--ignore-certificate-errors', '--disable-web-security', '--disable-features=IsolateOrigins,site-per-process', ], }); const page = await browser.newPage(); try { // 1. Login via Gatekeeper console.log('🔑 Authenticating...'); await page.goto(targetUrl, { waitUntil: 'domcontentloaded', timeout: 60000 }); console.log(` Waiting for potential Gatekeeper redirect...`); await new Promise((resolve) => setTimeout(resolve, 3000)); let isGatekeeper = null; try { isGatekeeper = await page.$('input[name="password"]'); } catch (e) { console.log('Navigation happened while checking gatekeeper:', e.message); } if (isGatekeeper) { await page.type('input[name="password"]', gatekeeperPassword); await Promise.all([ page.waitForNavigation({ waitUntil: 'domcontentloaded' }), page.click('button[type="submit"]'), ]); console.log(` Waiting for Login -> Home redirect...`); await new Promise((resolve) => setTimeout(resolve, 3000)); } // 2. Check Key Pages & Assets const pages = ['/', '/de/kontakt', '/en/contact', '/de/blog']; for (const p of pages) { console.log(`📄 Checking ${p}...`); const res = await page.goto(`${targetUrl}${p}`, { waitUntil: 'domcontentloaded' }).catch(e => { console.log(`Navigation caveat on ${p}:`, e.message); return null; }); // Allow Gatekeeper/Locale redirect to settle await new Promise(r => setTimeout(r, 2000)); if (res && res.status() !== 200 && res.status() !== 304 && res.status() !== 308 && res.status() !== 307) { console.log(`Warning: Page ${p} returned ${res.status()}`); } // Check for broken images (using in-page fetch to preserve session and avoid heavy navigation) const images = await page.$$eval('img', (imgs) => imgs.map((img) => img.src).filter(Boolean)); console.log(` Checking ${images.length} images...`); const brokenImages = await page.evaluate(async (urls) => { const broken: string[] = []; await Promise.all( urls.map(async (url) => { if (url.includes('openstreetmap.org') || url.includes('unpkg.com')) return; try { const res = await fetch(url, { method: 'GET' }); if (res.status >= 400) { // Diagnostic: check if the raw source is available const rawUrlMatch = url.match(/[?&]url=([^&]+)/); if (rawUrlMatch) { const rawPath = decodeURIComponent(rawUrlMatch[1]); const baseUrl = new URL(url).origin; const rawUrl = `${baseUrl}${rawPath}`; const rawRes = await fetch(rawUrl, { method: 'GET' }); broken.push(`${url} (Status: ${res.status}, Raw Status: ${rawRes.status})`); } else { broken.push(`${url} (Status: ${res.status})`); } } } catch (e) { broken.push(`${url} (Fetch failed)`); } }), ); return broken; }, images); if (brokenImages.length > 0) { throw new Error( `Found ${brokenImages.length} broken images on ${p}:\n${brokenImages.join('\n')}`, ); } } // 3. Test Contact Form console.log('📝 Testing Contact Form...'); await page.goto(`${targetUrl}/de/kontakt`, { waitUntil: 'domcontentloaded' }).catch(e => console.log('Navigation caveat:', e.message)); // Wait for the Suspense boundary to hydrate the form await page.waitForSelector('input[name="name"]', { visible: true, timeout: 15000 }); await page.type('input[name="name"]', 'Smoke Test'); await page.type('input[name="email"]', 'smoke@mintel.me'); await page.type('textarea[name="message"]', 'Automated smoke test submission.'); await Promise.all([ page.waitForSelector('[role="alert"]', { timeout: 15000 }), page.click('button[type="submit"]'), ]); const alertText = await page.$eval('[role="alert"]', (el) => el.textContent); console.log(`🔔 Alert: ${alertText}`); if (alertText?.toLowerCase().includes('error') || alertText?.toLowerCase().includes('fehler')) { throw new Error(`Form submission failed: ${alertText}`); } console.log('✅ Smoke test passed!'); process.exit(0); } catch (err: any) { console.error(`❌ Smoke test failed: ${err.message}`); process.exit(1); } finally { await browser.close(); } } run();