test(provider): ?provider=maplibre must swap the ENGINE, measured by request host
Some checks are pending
build-gate / build (push) Waiting to run
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:
parent
c9ed3b4e9a
commit
e1553df458
2 changed files with 100 additions and 0 deletions
99
internal/check-provider-switch.mjs
Normal file
99
internal/check-provider-switch.mjs
Normal 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}.`)
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in a new issue