test(provider): ?provider=maplibre must swap the ENGINE, measured by request host
Some checks are pending
build-gate / build (push) Waiting to run

Gates BUG-UPG100-IGNORES-THE-PROVIDER-SWITCH. my-upg-100-list builds its map RAW
with `new gl.Map` instead of going through useMapInstance, so the page-level
switch — which resolves inside that composable — could not reach it. The card
measured 19 requests to api.mapbox.com at ?provider=maplibre while five sibling
leaves swapped cleanly.

★ IT COUNTS NETWORK REQUESTS BY HOST, not globals. `window.mapboxgl` can be
present because the leaf page preloads it while the map is actually DRAWN by
maplibre, and the reverse is equally possible. What the switch exists for is not
loading a proprietary engine and not calling its servers, and only the request
log shows that.

The allowance is DERIVED from the leaf's own HTML — count the distinct
api.mapbox.com urls the generated page declares, plus one for a stylesheet that
gets fetched twice — rather than hardcoded. Asserting merely "fewer than the
default" would pass a half-fix at 18 of 19; hardcoding 5 would pass a leaf that
preloads 4 and leaks 1 runtime call.

★ THE GATE'S OWN FIRST VERSION MEASURED NOTHING AND PASSED. `const URL =
process.argv[2]` shadowed the global URL constructor, so every
`new URL(request.url())` threw, the host tally stayed empty, and it cheerfully
reported "0 mapbox requests" on a page making 19. It now names the variable LEAF,
counts what it could not parse, and FAILS if the default run records no requests
at all — because a silently empty tally is indistinguishable from success.

Passing on three leaves today: upg-100-list 19 -> 5, doxa-simple-map 21 -> 4,
countries-map 24 -> 4, with every residual being the page's own <link>/<script>
preloads. Confirmed to fail when the allowance is tightened by one.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 04:47:42 +00:00
parent c9ed3b4e9a
commit e1553df458
2 changed files with 100 additions and 0 deletions

View file

@ -0,0 +1,99 @@
// check-provider-switch — ?provider=maplibre must actually SWAP THE ENGINE.
//
// The bug this gates (BUG-UPG100-IGNORES-THE-PROVIDER-SWITCH): my-upg-100-list
// builds its map RAW with `new gl.Map` instead of going through useMapInstance, so
// the page-level switch — which resolves inside that composable — could not reach
// it. MEASURED at the time: five sibling leaves swapped cleanly while this one
// stayed on Mapbox and made 19 requests to api.mapbox.com.
//
// ★ IT COUNTS NETWORK REQUESTS BY HOST, not globals. `window.mapboxgl` can be
// present because a page preloads it, and a map can still be DRAWN by maplibre —
// or the reverse. What the switch is for is not loading a proprietary engine and
// not calling its tile servers, and only the request log shows that.
//
// Usage: node internal/check-provider-switch.mjs <leafUrl>
import { chromium } from 'playwright'
// ⚠ NOT named `URL` — that shadows the global URL constructor, so every
// `new URL(request.url())` below throws and the host tally stays EMPTY while the
// gate happily reports 0 mapbox requests and PASSES. Measured: it did exactly
// that, hiding 4 real requests to events.mapbox.com.
const LEAF = process.argv[2]
if (!LEAF) { console.error('usage: check-provider-switch.mjs <leafUrl>'); process.exit(2) }
async function run(provider) {
const b = await chromium.launch({ args: ['--use-gl=swiftshader', '--enable-unsafe-swiftshader'] })
const pg = await b.newPage({ viewport: { width: 1280, height: 900 } })
const hosts = new Map()
let unparsed = 0
pg.on('request', (r) => {
try {
const h = new URL(r.url()).hostname
if (!h) { unparsed += 1; return }
hosts.set(h, (hosts.get(h) || 0) + 1)
} catch { unparsed += 1 }
})
const errs = []
pg.on('pageerror', (e) => errs.push(String(e).slice(0, 120)))
const url = provider ? `${LEAF}${LEAF.includes('?') ? '&' : '?'}provider=${provider}` : LEAF
await pg.goto(url, { waitUntil: 'load', timeout: 45000 })
await pg.waitForTimeout(12000)
const globals = await pg.evaluate(() => ({
mapbox: typeof window.mapboxgl !== 'undefined',
maplibre: typeof window.maplibregl !== 'undefined',
}))
await b.close()
const count = (needle) => [...hosts.entries()]
.filter(([h]) => h.includes(needle)).reduce((a, [, n]) => a + n, 0)
// Report what could NOT be parsed rather than swallowing it: a silently empty
// tally is how this gate first passed while measuring nothing at all.
return { mapboxReq: count('mapbox.com'), globals, errs, unparsed, total: [...hosts.values()].reduce((a, n) => a + n, 0) }
}
const def = await run(null)
const ml = await run('maplibre')
// DERIVED from the leaf's own HTML, not a hardcoded number: count the distinct
// api.mapbox.com references the generated page carries, then allow a little slack
// because a stylesheet can be fetched more than once (the upg-100 leaf resolves 4
// distinct urls but issues 5 requests — one css is re-fetched). Hardcoding 5 would
// pass a leaf that legitimately preloads 4 and leaks 1 runtime call.
const pageHtml = await (await fetch(LEAF)).text()
const declared = new Set((pageHtml.match(/https:\/\/api\.mapbox\.com[^"')]+/g) || []))
const PRELOAD_ALLOWANCE = Number(process.env.PROVIDER_PRELOADS ?? (declared.size + 1))
console.log(` page preloads : ${declared.size} distinct api.mapbox.com url(s) in the leaf HTML`)
console.log(` default : ${def.mapboxReq} mapbox / ${def.total} total request(s) `
+ `[mapboxgl=${def.globals.mapbox} maplibregl=${def.globals.maplibre}]`)
console.log(` ?provider=maplibre: ${ml.mapboxReq} mapbox / ${ml.total} total request(s) `
+ `[mapboxgl=${ml.globals.mapbox} maplibregl=${ml.globals.maplibre}]`)
const fails = []
if (!ml.globals.maplibre) {
fails.push('?provider=maplibre did not load the maplibre engine at all')
}
// The RESIDUAL SET is the leaf page's own <link>/<script> preloads — mapbox-gl and
// the geocoder, referenced in the generated index.html header. Those fire before any
// Vue code runs, so no runtime switch can suppress them; removing them is a separate
// concern (the page template), not this bug. What the switch must achieve is that NO
// RUNTIME request goes to Mapbox: measured 19 -> 5 on the upg-100 leaf, and all 5 are
// in the HTML. Asserting "fewer than default" would pass a half-fix at 18.
if (ml.mapboxReq > PRELOAD_ALLOWANCE) {
fails.push(`?provider=maplibre made ${ml.mapboxReq} mapbox request(s), more than the `
+ `${PRELOAD_ALLOWANCE} page preloads — ${ml.mapboxReq - PRELOAD_ALLOWANCE} are RUNTIME `
+ 'calls the switch failed to redirect')
}
if (def.mapboxReq <= PRELOAD_ALLOWANCE) {
fails.push(`the default run made only ${def.mapboxReq} mapbox request(s) — the map is probably `
+ 'not rendering, so this comparison proves nothing')
}
if (!def.total) fails.push('no requests were recorded at all — the tally is broken, not the page')
if (ml.errs.length) fails.push(`${ml.errs.length} page error(s) under maplibre: ${ml.errs[0]}`)
if (fails.length) {
console.error(`\nPROVIDER-SWITCH: FAIL — ${fails.length} problem(s)`)
for (const f of fails) console.error(' - ' + f)
process.exit(1)
}
console.log(`\nPROVIDER-SWITCH: PASS — maplibre loads and mapbox traffic drops `
+ `${def.mapboxReq} -> ${ml.mapboxReq}.`)

View file

@ -57,6 +57,7 @@
"build:unlocked": "bun run gates:pre && bun run library-index && bun run check:library-index && bun run clean && bun run token && bun run build:bundles && bun run tree && bun run gates:post && bun run gallery",
"check:card-deck": "node internal/check-card-deck.mjs",
"check:poster-raster": "node internal/check-poster-raster.mjs",
"check:provider-switch": "node internal/check-provider-switch.mjs",
"check:mfe-two-tab": "node internal/check-mfe-two-tab.mjs",
"check:share-policy": "node internal/check-share-policy.mjs",
"check:zone-census": "node internal/check-zone-census.mjs",