// CI-only connectivity diagnostics (globalSetup runs after the e2e stack is ready). // Evidence for the Chromium ERR_SSL_PROTOCOL_ERROR hunt: compares node-side resolution/HTTP // against Chromium's own navigation, hostname vs static IP. Never fails the run. import { promises as dnsPromises } from 'node:dns'; import { chromium } from '@playwright/test'; export default async function ciDiagnostics() { if (process.env.PW_REMOTE_BROWSER !== '0') return; // local runs: no-op const base = process.env.E2E_BASE_URL ?? ''; try { console.log('[diag] node resolve4(app):', JSON.stringify(await dnsPromises.resolve4('app').catch((e) => `failed: ${e.code}`))); try { const r = await fetch(`${base}/api/version`); console.log('[diag] node fetch', base, '->', r.status, JSON.stringify(await r.json().catch(() => null))); } catch (e) { console.log('[diag] node fetch failed:', String(e)); } const browser = await chromium.launch(); try { for (const url of ['http://app:3000/', base]) { const page = await (await browser.newContext()).newPage(); try { await page.goto(url, { timeout: 15_000 }); console.log('[diag] chromium goto', url, '-> OK, title:', await page.title().catch(() => '?')); } catch (e) { console.log('[diag] chromium goto', url, '-> FAILED:', String(e).split('\n')[0]); } await page.context().close(); } } finally { await browser.close(); } } catch (e) { console.log('[diag] diagnostics error (non-fatal):', String(e)); } }