* Drive a single bundle through the lifecycle test. Returns a result * record; never throws on bundle-side failure (those become * ``ok: false``). Throws only on infrastructure issues (harness * failed to start, Chromium failed to launch).
({ name, bundle })
| 214 | * failed to start, Chromium failed to launch). |
| 215 | */ |
| 216 | async function runBundle({ name, bundle }) { |
| 217 | const workDir = fs.mkdtempSync(path.join(os.tmpdir(), `cn1-lifecycle-${name}-`)); |
| 218 | const serveDir = path.join(workDir, 'served'); |
| 219 | const bundleDir = path.join(workDir, 'bundle'); |
| 220 | const logFile = path.join(workDir, 'browser.log'); |
| 221 | const urlFile = path.join(workDir, 'url.txt'); |
| 222 | |
| 223 | console.log(`[lifecycle] ${name}: materialising ${bundle}`); |
| 224 | materializeBundle(bundle, bundleDir); |
| 225 | const indexRoot = locateIndexRoot(bundleDir); |
| 226 | if (!indexRoot) { |
| 227 | return { name, bundle, ok: false, milestones: {}, reason: 'bundle has no index.html' }; |
| 228 | } |
| 229 | copyTree(indexRoot, serveDir); |
| 230 | injectProbeScript(path.join(serveDir, 'index.html')); |
| 231 | |
| 232 | console.log(`[lifecycle] ${name}: starting harness`); |
| 233 | let harness; |
| 234 | try { |
| 235 | harness = await startHarness(serveDir, logFile, urlFile); |
| 236 | } catch (err) { |
| 237 | return { name, bundle, ok: false, milestones: {}, reason: `harness start failed: ${err.message}` }; |
| 238 | } |
| 239 | |
| 240 | const url = decorateUrl(harness.url); |
| 241 | console.log(`[lifecycle] ${name}: serving at ${url}`); |
| 242 | |
| 243 | // Capture every lifecycle marker and the most-recent FIRST_FAILURE |
| 244 | // so the report can pinpoint where the bundle stalled. |
| 245 | const lifecycle = []; |
| 246 | let firstFailure = null; |
| 247 | let pageError = null; |
| 248 | |
| 249 | const browser = await chromium.launch({ |
| 250 | headless: true, |
| 251 | args: [ |
| 252 | '--autoplay-policy=no-user-gesture-required', |
| 253 | '--disable-web-security', |
| 254 | '--allow-file-access-from-files' |
| 255 | ] |
| 256 | }); |
| 257 | |
| 258 | let result; |
| 259 | try { |
| 260 | const page = await browser.newPage({ |
| 261 | viewport: { width: 375, height: 667 }, |
| 262 | deviceScaleFactor: 2 |
| 263 | }); |
| 264 | |
| 265 | page.on('console', msg => { |
| 266 | const text = msg.text(); |
| 267 | if (text.indexOf('PARPAR-LIFECYCLE:') >= 0) { |
| 268 | lifecycle.push(text); |
| 269 | } |
| 270 | if (text.indexOf('PARPAR:DIAG:FIRST_FAILURE') >= 0) { |
| 271 | // Aggregate into a single record; the runtime emits |
| 272 | // ``category`` / ``methodId`` / ``receiverClass`` as separate |
| 273 | // lines. Last value wins, which is fine since the runtime |
no test coverage detected