diff --git a/internal/check-poster-print-fallback.mjs b/internal/check-poster-print-fallback.mjs index 968a13c3a..e2588595c 100644 --- a/internal/check-poster-print-fallback.mjs +++ b/internal/check-poster-print-fallback.mjs @@ -124,9 +124,18 @@ page.on('pageerror', (e) => errs.push(String(e).slice(0, 140))) // when that generator cannot load (a policy that refuses both the blob and the classic // script, a failed integrity check, a print while offline), so the generator is blocked // too, and the .html is the one way through. +// ★ ASSERT THE OUTCOME, NOT THE REQUEST (2026-09-29). A poison run answered 404 to posters/.html +// for the browser only, and this gate still PASSED: it counted the REQUEST, and its failure pattern did +// not match what the wall then says ("poster not found (404)"). So it could not fail on the one path it +// exists for. It now requires the .html to ANSWER 200, a print frame to actually OPEN (counted at +// creation, the degrade gate's technique), and reads "not found" as the failure it is. let webpBlocked = 0 let htmlServed = 0 +let htmlOk = 0 +let printFrames = 0 let generatorBlocked = 0 +page.on('response', (r) => { if (/\/posters\/[^/?]+\.html(\?|$)/.test(r.url()) && r.status() === 200) htmlOk += 1 }) +await page.exposeFunction('__notePrintFrame', () => { printFrames += 1 }) await page.route('**/posters/*.webp', (route) => { webpBlocked += 1; route.abort() }) await page.route('**/posters/*.html', (route) => { htmlServed += 1; route.continue() }) await page.route(/\/lib\/poster-render(\.global)?\.js(\?|$)/, (route) => { generatorBlocked += 1; route.abort() }) @@ -147,6 +156,18 @@ await page.waitForTimeout(8000) await page.evaluate(() => { window.__HUD = [] window.print = () => {} + // Count print frames AT CREATION: the frame self-removes on afterprint, so polling finds none. + const realAppend = Node.prototype.appendChild + Node.prototype.appendChild = function (n) { + const r = realAppend.call(this, n) + try { + if (n && n.tagName === 'IFRAME') { + window.__notePrintFrame() + try { n.contentWindow.print = () => {} } catch { /* cross-realm */ } + } + } catch { /* never break the page to observe it */ } + return r + } const walk = (root, out = []) => { for (const el of root.querySelectorAll('*')) { out.push(el); if (el.shadowRoot) walk(el.shadowRoot, out) } return out @@ -154,7 +175,7 @@ await page.evaluate(() => { window.__readHud = () => walk(document) .filter((e) => e.childElementCount === 0 && e.tagName !== 'SCRIPT' && e.tagName !== 'STYLE') .map((e) => (e.textContent || '').trim()) - .filter((t) => t && /generating|preparing|saving|not available|failed|blank|error/i.test(t)) + .filter((t) => t && /generating|preparing|saving|not found|not available|failed|blank|error/i.test(t)) setInterval(() => { for (const t of window.__readHud()) if (!window.__HUD.includes(t)) window.__HUD.push(t) }, 120) }) @@ -198,7 +219,7 @@ if (clicked.ok) { hud = await page.evaluate(() => window.__HUD || []) // Terminal states: an explicit failure, or "generating" seen AND no longer showing. const live = await page.evaluate(() => window.__readHud()) - const failing = hud.some((t) => /not available|failed|blank|error/i.test(t)) + const failing = hud.some((t) => /not found|not available|failed|blank|error/i.test(t)) const generated = hud.some((t) => /generating/i.test(t)) if (failing) { settled = true; break } if (generated && !live.some((t) => /generating|preparing|saving/i.test(t))) { settled = true } @@ -219,8 +240,8 @@ if (hud.some((t) => /Poster not available yet/i.test(t))) { fails.push('the print preview opened on "Poster not available yet" — the old behaviour: ' + 'the .webp 404ed and the rasterise fallback did not run') } -if (hud.some((t) => /not available|failed|blank|error/i.test(t))) { - fails.push(`the print path ended in an error state: ${JSON.stringify(hud.filter((t) => /not available|failed|blank|error/i.test(t)))}`) +if (hud.some((t) => /not found|not available|failed|blank|error/i.test(t))) { + fails.push(`the print path ended in an error state: ${JSON.stringify(hud.filter((t) => /not found|not available|failed|blank|error/i.test(t)))}`) } // With EVERY .webp aborted, a clean run is only possible via the rasteriser — so the // "generating the poster" phase MUST have been entered. Its absence means the preview @@ -238,13 +259,20 @@ if (webpBlocked === 0) { } if (htmlServed === 0) { fails.push('no posters/*.html was fetched — the rasterise fallback never ran') +} else if (htmlOk === 0) { + // THE OUTCOME, not the attempt: requested is not loaded, and a 404 source rasterises nothing. + fails.push(`posters/*.html was requested ${htmlServed} time(s) but never answered 200 — the fallback had no source to rasterise`) +} +if (clicked.ok && printFrames === 0) { + fails.push('no print frame opened — the print path never reached the dialog, so nothing was printed') } if (errs.length) fails.push(`${errs.length} page error(s): ${errs[0]}`) console.log('POSTER-PRINT-FALLBACK') console.log(` wall : ${WALL}`) console.log(` .webp requests : ${webpBlocked} (all aborted — the default-drop condition)`) -console.log(` .html requests : ${htmlServed}`) +console.log(` .html requests : ${htmlServed} (${htmlOk} answered 200)`) +console.log(` print frames : ${printFrames}`) console.log(` generator : ${generatorBlocked} request(s) aborted${generatorBlocked ? '' : ' (this wall generated nothing first)'}`) console.log(` print button : ${clicked.ok ? 'found and clicked' : 'NOT FOUND'}`) console.log(` HUD : ${hud.length ? JSON.stringify(hud) : '(nothing)'}`)