From 61866f3e06d1fec1a89614f81eefe8350a8e1bae Mon Sep 17 00:00:00 2001 From: doxa-maps-88655c70 <88627970+doxa-maps-88655c70@users.noreply.github.com> Date: Wed, 23 Sep 2026 15:13:14 +0000 Subject: [PATCH] Make the interop harness see through shadow DOM, and wait long enough The mobile pair reported "composite did not mount (map=false, cards=24)" on a tree verified SETTLED both before and after the run, so it read as a real defect. It was not. A direct browser probe of the same leaf found the composite completely healthy: both children mounted, a 1440x856 canvas PAINTED, 0 console errors, 0 failed requests, body 3168px. Two causes, and the asymmetry with the desktop pair is what hid them. SHADOW DOM DOES NOT PIERCE. The gate used document.querySelector ("doxa-research-map"), a single-level query. The desktop gate's document.querySelector("dashboard-plus-grid") works only because that element IS the top-level custom element on its own leaf. Here is nested inside the composite's shadow root, where a top-level selector cannot reach it. The harness already carries __walk(), a recursive shadowRoot walker, and simply was not using it on that line. THREE SECONDS IS NOT LONG ENOUGH. The direct probe needed ~22s before the nested map had mounted and painted. A composite that lazily injects a child bundle's script, waits for customElements, then initialises a GL map does not finish in 3s here. Now it polls up to 30s for both halves. The failure message also reports the custom tags it DID find when it gives up, because "map=false" alone cannot distinguish "never mounted" from "mounted somewhere this query cannot reach" -- which is exactly the distinction that cost a tick. A gate that cries wolf on a healthy surface gets ignored, and then it cannot report the real thing. Verified: verify:interop now PASSES 4/4. Mobile grid search "India" -> Total: 941; map mounts independently. Desktop unchanged -- facet -> "Showing 1,309", and row selection still moves the camera 28.000,18.000@1.45 -> 73.666,13.162@2.48. check:mjs-parse passes. --- internal/acceptance/interop-harness.mjs | 38 +++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/internal/acceptance/interop-harness.mjs b/internal/acceptance/interop-harness.mjs index d31e8d497..91ce100e4 100644 --- a/internal/acceptance/interop-harness.mjs +++ b/internal/acceptance/interop-harness.mjs @@ -355,9 +355,43 @@ async function runMobile(browser) { await page.waitForTimeout(3000) await reinject(page) - const mounted = await page.evaluate('({ map: !!document.querySelector("doxa-research-map"), cards: __cardCount() })') + // ★ FALSE NEGATIVE FIXED 2026-09-23. This mount gate reported "composite did not mount + // (map=false, cards=24)" on a tree that was verified SETTLED both before and after the run — so it + // read as a real defect, and it was not. A direct browser probe of the same leaf found the + // composite completely healthy: both children mounted, a 1440x856 canvas PAINTED, 0 console + // errors, 0 failed requests, body 3168px. + // + // TWO CAUSES, and the asymmetry with the desktop pair is what hid them: + // + // 1. SHADOW DOM DOES NOT PIERCE. This used `document.querySelector("doxa-research-map")`, a + // SINGLE-LEVEL query. The desktop gate's `document.querySelector("dashboard-plus-grid")` works + // only because that element IS the top-level custom element on its own leaf page. Here + // is NESTED INSIDE the composite's shadow root, where a top-level selector + // cannot see it. The harness already carries __walk(), a recursive shadowRoot walker, and + // simply was not using it on this line. + // + // 2. THREE SECONDS IS NOT LONG ENOUGH. The direct probe needed ~22s before the nested map had + // mounted and painted. A composite that lazily injects a child bundle's script, waits for + // customElements, then initialises a GL map does not finish in 3s on this box. + // + // ★ WHY THIS MATTERED MORE THAN A FLAKY TEST: the harness exists to catch interop regressions, and + // it was reporting the mobile pair as broken while the product was fine. A gate that cries wolf on + // a healthy surface gets ignored, and then it cannot report the real thing. + const mounted = await page.waitForFunction( + '(() => { const m = __walk("doxa-research-map")[0]; const c = __cardCount();' + + ' return (m && c > 0) ? { map: true, cards: c } : null })()', + { timeout: 30000 } + ).then(h => h.jsonValue()).catch(async () => { + // Report what was ACTUALLY there when it gave up — "map=false" alone cannot distinguish + // "never mounted" from "mounted somewhere this query cannot reach". + return page.evaluate( + '({ map: !!__walk("doxa-research-map")[0], cards: __cardCount(),' + + ' deepTags: [...new Set(__walk("*").map(e => e.tagName.toLowerCase()).filter(t => t.includes("-")))] })' + ) + }) if (!mounted.map || mounted.cards === 0) { - fail(`${PAIR}: composite did not mount (map=${mounted.map}, cards=${mounted.cards}) — cannot measure`) + fail(`${PAIR}: composite did not mount (map=${mounted.map}, cards=${mounted.cards}` + + `${mounted.deepTags ? `, custom tags found: ${mounted.deepTags.join(', ') || 'none'}` : ''}) — cannot measure`) await page.close(); return }