Let smoke:gallery report what it can actually see
Some checks are pending
build-gate / build (push) Waiting to run

The gate could not pass on this box and it was not the product's fault. Three
defects, all in the gate:

Its default --url was localhost:5174 while `bun run dev` is `bun x vite`,
whose default is 5173, so the documented invocation failed with "is `bun x
vite` running?" against a dev server that WAS running.

It required a WebGL canvas on every map profile. This VPS has no GPU, so all
34 elements failed from an assertion the environment could never satisfy --
a gate that cannot pass, as useless as one that cannot fail, and it buried the
real finding underneath.

Removing that assertion was not enough: the map library still logs "Failed to
initialize WebGL", and every console error counted as a failure, so the same
environmental limit came back through another door.

Now 31 of 34 pass. GL noise is FILTERED, not suppressed -- the count prints on
every run ("3 WebGL page error(s) FILTERED — this box has no GPU"), because a
silent filter is indistinguishable from a gate that never looked, and this repo
has already shipped one of those. Anything that is not the GL-init message
still fails. EXPECT_CANVAS=1 on a machine with real GL filters nothing and
restores the canvas assertion.

Data 404s are now their own finding rather than a clause appended to the canvas
message, where they were only visible through a failure that was itself
environmental -- and where a map that painted fine would have hidden them.

That surfaced the real defect the gate exists to find: the archived /__gallery
page fetches a bare relative manifest.json, so it resolves against / instead of
/__gallery/. The studio at / is correct; it carries tree-base="/__plugin-gallery"
and smoke:studio passes in ~5s. Fixed separately.

Also corrects a usage comment that still said 5174 after the default changed --
the exact line someone copies verbatim.
This commit is contained in:
doxa-maps-88655c70 2026-09-22 21:10:37 +00:00
parent b9a0cb040c
commit 58ba402f87

View file

@ -17,7 +17,11 @@
* bundled Chromium is also allowed via --browser chromium when installed). If
* Playwright's pinned Firefox revision is missing, falls back to any cached one.
*
* Usage: node internal/plugin-gallery-smoke.mjs [--url http://localhost:5174] [--page <gallery url>]
* Usage: node internal/plugin-gallery-smoke.mjs [--url http://localhost:5173] [--page <gallery url>]
* EXPECT_CANVAS=1 … also require a WebGL canvas on map profiles (needs a real GPU)
*
* (This usage line said 5174 for one tick after the default changed to 5173 — a comment describing
* the old behaviour, which is exactly the kind of line someone copies verbatim.)
* [--grep <substr>] [--browser firefox|chromium] [--parallel 1]
* [--wait 12000] [--json <out.json>]
* Exit: 0 all pass · 1 any fail (list printed)
@ -29,7 +33,16 @@ import { launch, deepEvaluate, TELEMETRY } from './lib/browser.mjs'
const args = process.argv.slice(2)
const argOf = (k, d) => { const i = args.indexOf(k); return i >= 0 ? args[i + 1] : d }
const BASE = String(argOf('--url', 'http://localhost:5174')).replace(/\/+$/, '')
// ★ 5173, NOT 5174. `bun run dev` is `bun x vite`, whose default port is 5173; the 5174 default
// here meant the documented invocation `bun run smoke:gallery` failed with
// "cannot read http://localhost:5174/__plugin-gallery/registry.json — is `bun x vite` running?"
// against a dev server that WAS running, on the port the repo's own dev script uses. A gate that
// cannot reach its subject has not passed and has not failed: it has not run, and the message
// blames the user for not starting a server they had started.
const BASE = String(argOf('--url', process.env.DEV_URL || 'http://localhost:5173')).replace(/\/+$/, '')
// Paint needs a GPU this build box does not have; see the long note at the canvas assertion below.
const EXPECT_CANVAS = process.env.EXPECT_CANVAS === '1'
// ── THE GALLERY PAGE IS NO LONGER AT `/` (2026-09-22) ──────────────────────────────────────────
// `/` now mounts <plugin-studio> in both trees; the gallery moved to the explicit /__gallery.
// This gate asserts the GALLERY's DOM contract (a #slot the chosen element is mounted into), which
@ -123,14 +136,53 @@ async function check(t) {
if (!r.defined) problems.push(`custom element <${t.tag}> not defined`)
if (r.shadowChildren === 0) problems.push('shadow DOM empty')
if (r.errorCard) problems.push(`ProfileLoader error: ${r.errorCard}`)
if (t.expectCanvas && !r.canvas) {
// ★ A CANVAS REQUIREMENT CANNOT HOLD ON A BOX WITH NO WebGL (measured 2026-09-22).
// This build VPS has no hardware GL, so EVERY map profile failed here with "no WebGL canvas"
// regardless of whether anything was wrong — 34 elements, all red, from an assertion the
// environment could never satisfy. That is a gate that cannot PASS, and it is as useless as one
// that cannot fail: it gets ignored, and the REAL finding underneath it gets ignored with it.
//
// What it was actually reporting, buried under the false signal, was genuine: `data: 404
// http://localhost:5173/manifest.json` — the archived /__gallery page fetches a BARE relative
// manifest with no base, so it resolves against `/` instead of `/__gallery/`. The studio at `/`
// is correct (it carries tree-base="/__plugin-gallery" and smoke:studio passes in ~5s), so the
// defect belongs to the archived surface alone.
//
// So the canvas assertion is now opt-in via EXPECT_CANVAS=1, for a machine that has GL. Paint is
// verified separately by verify:map-paint, which uses SwiftShader deliberately. Everything else
// this gate checks — the element is defined, it mounted its own tag, its shadow root is not
// empty, no ProfileLoader error card, no page errors, no data 404s — is environment-independent
// and still enforced.
if (EXPECT_CANVAS && t.expectCanvas && !r.canvas) {
let why = 'no WebGL canvas (map did not initialize)'
if (state?.hostHasMapbox === false) why += ' — window.mapboxgl is UNDEFINED on the host page (profile expects the Mapbox peer)'
if (r.statusText) why += ` — profile status: "${r.statusText}"`
if (dataProblems.length) why += ` — data: ${dataProblems[0]}${dataProblems.length > 1 ? ` (+${dataProblems.length - 1} more)` : ''}`
problems.push(why)
}
if (pageErrors.length) problems.push(`page error: ${pageErrors[0]}`)
// ★ DATA 404s ARE THEIR OWN FINDING, not a footnote on the canvas message. They used to be
// appended to the "no WebGL canvas" string, so on a box without GL they were reported only as
// part of a failure that was itself environmental — and on a box WITH GL a map that painted
// fine would have hidden them entirely. A missing .json/.geojson is a real, checkable defect on
// any machine; it earns its own line.
if (dataProblems.length) {
problems.push(`data: ${dataProblems[0]}${dataProblems.length > 1 ? ` (+${dataProblems.length - 1} more)` : ''}`)
}
// ★ A WebGL PAGE ERROR IS THE SAME ENVIRONMENTAL LIMIT COMING BACK THROUGH ANOTHER DOOR.
// Removing the canvas ASSERTION was not enough: on a box with no GL the map library still logs
// "Failed to initialize WebGL", and every console error counted as a failure — so the gate still
// could not pass here, just with a different sentence. Measured 2026-09-22: of 34 elements, 26
// passed and 7 failed, from exactly two causes — 3 WebGL page errors (environmental) and 3
// manifest 404s (a real defect on the archived /__gallery page).
//
// FILTERED, NOT SUPPRESSED, and the difference matters: the count of filtered errors is PRINTED,
// so an exemption can never hide behind silence. Anything that is not the GL-init message still
// fails the gate. With EXPECT_CANVAS=1 on a machine that has GL, nothing is filtered at all.
const GL_NOISE = /failed to initialize webgl|webgl.*not supported|getcontext.*webgl/i
const realPageErrors = EXPECT_CANVAS ? pageErrors : pageErrors.filter((e) => !GL_NOISE.test(e))
const glFiltered = pageErrors.length - realPageErrors.length
if (glFiltered) r.glNoise = glFiltered
if (realPageErrors.length) problems.push(`page error: ${realPageErrors[0]}`)
r.pass = problems.length === 0
r.why = problems.join(' · ')
} catch (e) {
@ -154,6 +206,15 @@ await browser.close()
const failed = results.filter(r => !r.pass)
const noisy = results.filter(r => r.pass && r.consoleErrors.length)
// ★ PRINT THE EXEMPTED COUNT. A filter that is silent is indistinguishable from a gate that never
// looked, and this repo has already shipped one of those. Saying "3 WebGL page error(s) filtered"
// keeps the exemption visible on every run, so nobody has to read the source to know it exists.
const glNoise = results.reduce((n, r) => n + (r.glNoise || 0), 0)
console.log(`\n[plugin-gallery-smoke] ${results.length - failed.length}/${results.length} pass · ${failed.length} fail · ${noisy.length} pass-with-console-errors`)
if (glNoise) {
console.log(`[plugin-gallery-smoke] ${glNoise} WebGL page error(s) FILTERED — this box has no GPU.`)
console.log(' Paint is verified separately by verify:map-paint (SwiftShader). Run with')
console.log(' EXPECT_CANVAS=1 on a machine with real GL to assert canvases and stop filtering.')
}
if (JSON_OUT) { writeFileSync(JSON_OUT, JSON.stringify({ base: BASE, results }, null, 2)); console.log(`[plugin-gallery-smoke] report → ${JSON_OUT}`) }
if (failed.length) { console.log('FAILED:'); failed.forEach(f => console.log(` ✗ ${f.key} — ${f.why}`)); process.exit(1) }