import assert from 'node:assert/strict'; import fs from 'node:fs/promises'; import http from 'node:http'; import net from 'node:net'; import path from 'node:path'; import { spawn } from 'node:child_process'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { setTimeout as delay } from 'node:timers/promises'; const root = fileURLToPath(new URL('../', import.meta.url)); const moduleName = process.env.PLAYWRIGHT_MODULE ? pathToFileURL(path.resolve(process.env.PLAYWRIGHT_MODULE)).href : 'playwright'; const expectedMode = process.env.FORM_A_BROWSER_MODE === 'off' ? 'off' : 'mock'; const campaignRoutes = ['/lp/aba-therapy', '/lp/aba-insurance', '/lp/aba-ahcccs', '/lp/aba-private-pay']; const earlyInterventionRoute = '/lp/early-intervention'; const routes = expectedMode === 'off' ? [...campaignRoutes, earlyInterventionRoute] : ['/contact', '/es/contact', ...campaignRoutes, earlyInterventionRoute]; const output = await fs.mkdtemp('/tmp/aia-form-a-preview-'); const evidence = { mode: expectedMode, routes, widths: [390, 1440], checks: 0, failures: [], blocked: [], productionAttempts: [], screenshots: [], status: 'failed' }; const reserve = net.createServer(); await new Promise((resolve, reject) => { reserve.once('error', reject); reserve.listen(0, '127.0.0.1', resolve); }); const port = reserve.address().port; await new Promise((resolve) => reserve.close(resolve)); const origin = `http://127.0.0.1:${port}`; const isProduction = (url) => /(?:jotform|api\.azinstitute4autism\.com|google-analytics|googletagmanager|doubleclick)/i.test(url.hostname + url.pathname); let browser; let preview; try { const { chromium } = await import(moduleName); browser = await chromium.launch(process.env.CHROMIUM_EXECUTABLE ? { headless: true, executablePath: process.env.CHROMIUM_EXECUTABLE } : { headless: true }); preview = spawn(process.execPath, ['node_modules/astro/bin/astro.mjs', 'preview', '--host', '127.0.0.1', '--port', String(port)], { cwd: root, env: { ...process.env, ASTRO_TELEMETRY_DISABLED: '1' }, stdio: 'ignore' }); const reachable = () => new Promise((resolve) => { const request = http.get(origin + '/', (response) => { response.resume(); resolve(response.statusCode === 200); }); request.setTimeout(500, () => request.destroy()); request.on('error', () => resolve(false)); }); for (let i = 0; i < 60 && !(await reachable()); i++) await delay(250); assert.equal(await reachable(), true); for (const width of evidence.widths) { const context = await browser.newContext({ viewport: { width, height: width < 500 ? 844 : 900 }, serviceWorkers: 'block' }); await context.routeWebSocket('**/*', (socket) => socket.close()); await context.route('**/*', (route) => { const request = route.request(); const url = new URL(request.url()); if (url.origin !== origin || !['GET', 'HEAD'].includes(request.method())) { const item = { origin: url.origin, path: url.pathname, method: request.method() }; evidence.blocked.push(item); if (isProduction(url)) evidence.productionAttempts.push(item); return route.abort(); } return route.continue(); }); const page = await context.newPage(); for (const route of routes) { evidence.checks++; try { const response = await page.goto(origin + route, { waitUntil: 'domcontentloaded', timeout: 15000 }); assert.equal(response?.status(), 200); if (route === earlyInterventionRoute) { assert.equal(await page.locator('[data-form-a-preview]').count(), 0); assert.equal(await page.locator('#consultation[data-forms-mode="off"]').count(), 1); assert.equal(await page.locator('#consultation button[type="button"][disabled]').count(), 1); assert.equal(await page.locator('[data-form-status-notice], #consultation-unavailable').count(), 0); assert.doesNotMatch(await page.locator('.qr-landing').textContent(), /(?:online )?(?:forms?|requests?|submissions?) (?:are |is )?(?:unavailable|not enabled)|detailed medical|diagnostic|insurance-member/i); assert.ok(await page.locator('a[href="tel:+14806877099"]').count() >= 1); assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1)); continue; } if (expectedMode === 'off') { assert.equal(await page.locator('[data-form-a-preview]').count(), 0); assert.equal(await page.locator('#lead-form[data-forms-mode="off"]').count(), 1); assert.equal(await page.locator('[data-form-status-notice], #campaign-form-status').count(), 0); assert.equal(await page.locator('#lead-form a[href^="tel:"]').count(), 1); assert.equal(await page.locator('#lead-form a[href="/privacy-policy"]').count(), 1); assert.doesNotMatch(await page.locator('#lead-form').textContent(), /(?:online )?(?:forms?|requests?|submissions?) (?:are |is )?(?:unavailable|not enabled)|detailed medical|diagnostic|insurance-member/i); assert.equal(await page.locator('#lead-form button[type="button"][disabled]').count(), 1); assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1)); continue; } const compact = route.startsWith('/lp/'); const fields = compact ? ['name', 'phone', 'email', 'preferredContact'] : ['name', 'phone', 'email', 'preferredContact', 'message']; const result = await page.evaluate(({ fields: expected, compact: isCompact }) => ({ enabled: document.querySelector('[data-form-a-preview]')?.dataset.formAPreviewEnabled, names: [...document.querySelectorAll('[data-form-a-preview] [name]')].map((el) => el.getAttribute('name')), forms: document.querySelectorAll('form').length, iframe: document.querySelectorAll('iframe').length, h1: document.querySelectorAll('main h1').length, width: document.documentElement.scrollWidth, expectedCount: expected.length, isCompact }), { fields, compact }); assert.equal(result.enabled, 'true'); assert.deepEqual(result.names, fields); assert.equal(result.forms, 0); assert.equal(result.iframe, 0); assert.equal(result.h1, 1); assert.ok(result.width <= width + 1, `overflow ${result.width}`); const preview = page.locator('[data-form-a-preview]'); if (route === '/es/contact') { assert.match(await preview.textContent(), /Su nombre|Método de contacto preferido/); assert.doesNotMatch(await preview.textContent(), /Your name|Preferred contact method/); } const expectedButtonText = route === '/contact' ? 'Submit' : route === '/es/contact' ? 'Enviar' : { '/lp/aba-therapy': 'Request a Free Consultation', '/lp/aba-insurance': 'Request Insurance Verification', '/lp/aba-ahcccs': 'Request a Benefits Check', '/lp/aba-private-pay': 'Discuss Private-Pay Options' }[route]; assert.equal(await preview.locator('[data-preview-status]').count(), 1); assert.equal(await preview.locator('.form-a-preview__caution').count(), compact ? 0 : 1); if (compact) { assert.equal(await page.locator('[data-form-status-notice]').count(), 1); assert.equal((await page.locator('[data-form-status-notice]').textContent()).match(/Preview only — nothing is submitted\./g)?.length, 1); assert.equal(await page.locator('[data-form-status-notice] a[href="tel:+14806877099"]').count(), 1); assert.equal(await page.locator('[data-form-status-notice] a[href="/privacy-policy"]').count(), 1); assert.doesNotMatch(await page.locator('#consultation').textContent(), /(?:online )?(?:forms?|requests?|submissions?) (?:are |is )?(?:unavailable|not enabled)|detailed medical|diagnostic|insurance-member/i); } const button = preview.locator('[data-preview-submit]'); assert.equal((await button.textContent()).trim(), expectedButtonText); const filledStyle = await button.evaluate((element) => { const style = getComputedStyle(element); return { background: style.backgroundColor, border: style.borderColor, borderWidth: style.borderWidth, color: style.color }; }); assert.equal(filledStyle.background, 'rgb(255, 189, 89)'); assert.equal(filledStyle.border, 'rgb(255, 189, 89)'); assert.equal(filledStyle.borderWidth, '2px'); assert.equal(filledStyle.color, 'rgb(37, 64, 128)'); await button.hover(); await delay(350); const hoverStyle = await button.evaluate((element) => { const style = getComputedStyle(element); return { background: style.backgroundColor, border: style.borderColor }; }); assert.equal(hoverStyle.background, 'rgba(0, 0, 0, 0)'); assert.equal(hoverStyle.border, 'rgb(255, 145, 77)'); await button.focus(); assert.notEqual(await button.evaluate((element) => getComputedStyle(element).outlineWidth), '0px'); await button.click(); assert.doesNotMatch(await preview.locator('[data-preview-status]').textContent(), /completed locally|terminó localmente/); await preview.locator('input[name="name"]').fill('Demo Reviewer'); await preview.locator('input[name="phone"]').fill('480-555-0100'); await preview.locator('select[name="preferredContact"]').selectOption('phone'); await button.click(); if (compact) assert.match(await preview.locator('[data-preview-status]').textContent(), /Preview only — nothing is submitted/); else assert.match(await preview.locator('[data-preview-status]').textContent(), /completed locally|terminó localmente/); await preview.locator('select[name="preferredContact"]').selectOption('email'); if (route === '/es/contact') { assert.equal( (await preview.locator('[id$="-email-requirement"]').textContent())?.trim(), '(obligatorio para contacto por correo)' ); } await button.click(); await assert.match(await preview.locator('[data-preview-status]').textContent(), /email|correo/i); await preview.locator('input[name="email"]').fill('demo@example.test'); await button.click(); if (compact) assert.match(await preview.locator('[data-preview-status]').textContent(), /Preview only — nothing is submitted/); else assert.match(await preview.locator('[data-preview-status]').textContent(), /completed locally|terminó localmente/); if ((route === '/contact' || route === '/lp/aba-therapy') && (width === 390 || width === 1440)) { const file = `${route === '/contact' ? 'contact' : 'campaign'}-${width}.png`; await page.screenshot({ path: path.join(output, file), fullPage: true }); evidence.screenshots.push({ route, width, file }); } } catch (error) { evidence.failures.push({ route, width, error: String(error.message) }); } } await context.close(); } if (expectedMode === 'mock') { const noJsContext = await browser.newContext({ viewport: { width: 390, height: 844 }, javaScriptEnabled: false, serviceWorkers: 'block' }); await noJsContext.route('**/*', (route) => { const request = route.request(); const url = new URL(request.url()); if (url.origin !== origin || !['GET', 'HEAD'].includes(request.method())) { const item = { origin: url.origin, path: url.pathname, method: request.method(), noJs: true }; evidence.blocked.push(item); if (isProduction(url)) evidence.productionAttempts.push(item); return route.abort(); } return route.continue(); }); for (const route of ['/contact', '/lp/aba-therapy']) { evidence.checks++; const page = await noJsContext.newPage(); const response = await page.goto(origin + route, { waitUntil: 'domcontentloaded', timeout: 15000 }); assert.equal(response?.status(), 200); assert.equal(await page.locator('[data-form-a-preview]').count(), 1); assert.equal(await page.locator('form').count(), 0); const bodyText = await page.locator('body').textContent(); if (route === '/contact') assert.match(bodyText, /JavaScript|JavaScript is required|Se requiere JavaScript/); else assert.match(bodyText, /Preview only — nothing is submitted/); } await noJsContext.close(); } evidence.status = evidence.failures.length || evidence.productionAttempts.length ? 'failed' : 'passed'; } catch (error) { evidence.error = String(error.message); } finally { await browser?.close(); if (preview && preview.exitCode === null) { preview.kill('SIGTERM'); await Promise.race([new Promise((resolve) => preview.once('exit', resolve)), delay(3000)]); if (preview.exitCode === null) preview.kill('SIGKILL'); } evidence.finishedAt = new Date().toISOString(); await fs.writeFile(path.join(output, 'results.json'), JSON.stringify(evidence, null, 2) + '\n'); console.log(JSON.stringify({ ...evidence, output }, null, 2)); if (evidence.status !== 'passed') process.exitCode = 1; }