diff --git a/scripts/check-forms.ts b/scripts/check-forms.ts index a28ee20f..9ed0f973 100644 --- a/scripts/check-forms.ts +++ b/scripts/check-forms.ts @@ -80,11 +80,48 @@ async function main() { const page = await browser.newPage(); - page.on('console', (msg) => console.log('💻 BROWSER CONSOLE:', msg.text())); - page.on('pageerror', (error) => console.error('💻 BROWSER ERROR:', error.message)); - page.on('requestfailed', (request) => { - console.error('💻 BROWSER REQUEST FAILED:', request.url(), request.failure()?.errorText); + let chunkErrorsDetected = false; + page.on('console', (msg) => { + const text = msg.text(); + console.log('💻 BROWSER CONSOLE:', text); + if (text.includes('ChunkLoadError') || text.includes('failed to load chunk')) { + chunkErrorsDetected = true; + } }); + page.on('pageerror', (error) => { + console.error('💻 BROWSER ERROR:', error.message); + if (error.message.includes('ChunkLoadError')) { + chunkErrorsDetected = true; + } + }); + page.on('requestfailed', (request) => { + const url = request.url(); + const failure = request.failure()?.errorText; + console.error('💻 BROWSER REQUEST FAILED:', url, failure); + if (url.endsWith('.js') && (failure === 'net::ERR_ABORTED' || failure?.includes('404'))) { + chunkErrorsDetected = true; + } + }); + + // Helper for resilient navigation with cache-busting fallback + const navigateWithRetry = async (url: string, label: string) => { + chunkErrorsDetected = false; + console.log(`\n🧪 Testing ${label} on: ${url}`); + + // First attempt + await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 }); + await delay(2000); // Allow initial JS execution + + if (chunkErrorsDetected) { + const cbUrl = `${url}${url.includes('?') ? '&' : '?'}cb=${Date.now()}`; + console.warn(` ⚠️ Assets failed to load (Varnish staleness suspected). Retrying with cache-buster: ${cbUrl}`); + chunkErrorsDetected = false; + await page.goto(cbUrl, { waitUntil: 'domcontentloaded', timeout: 30000 }); + } + + // Wait for network to settle slightly + await page.waitForNetworkIdle({ idleTime: 500, timeout: 10000 }).catch(() => {}); + }; // 3. Authenticate through Gatekeeper login form try { @@ -123,13 +160,11 @@ async function main() { // 4. Test Contact Form try { - console.log(`\n🧪 Testing Contact Form on: ${contactUrl}`); - await page.goto(contactUrl, { waitUntil: 'networkidle0', timeout: 30000 }); + await navigateWithRetry(contactUrl, 'Contact Form'); // Ensure React has hydrated completely - CI runners can be slow console.log(` ⏳ Waiting for hydration (5s)...`); - await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 5000))); - await page.waitForNetworkIdle({ idleTime: 1000, timeout: 20000 }).catch(() => {}); + await delay(5000); // Ensure form is visible and interactive try { @@ -191,13 +226,11 @@ async function main() { // 4. Test Product Quote Form try { - console.log(`\n🧪 Testing Product Quote Form on: ${productUrl}`); - await page.goto(productUrl, { waitUntil: 'networkidle0', timeout: 30000 }); + await navigateWithRetry(productUrl, 'Product Quote Form'); // Ensure React has hydrated completely console.log(` ⏳ Waiting for hydration (5s)...`); - await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 5000))); - await page.waitForNetworkIdle({ idleTime: 1000, timeout: 20000 }).catch(() => {}); + await delay(5000); // The product form uses dynamic IDs, so we select by input type in the specific form context try {