Add 5-capability verification gate: proves every capability level mounts, renders and is error-free
This commit is contained in:
parent
e4be3ee4ee
commit
79d46126de
2 changed files with 84 additions and 1 deletions
82
internal/verify-5-capabilities.mjs
Normal file
82
internal/verify-5-capabilities.mjs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* verify-5-capabilities.mjs — prove ALL FIVE capability levels actually render.
|
||||
*
|
||||
* ★ WHY: the card BUILD-DXMAPS-ALL-5-CAPS-WORKING-LOW-BUG was marked done with NO end-to-end check.
|
||||
* "Done is a passing test, not a field value" — this is that test. First run (2026-09-20) came back
|
||||
* 3/5: caps 1-3 clean, caps 4-5 failing on ONE shared bug (vue-i18n code 27), which is exactly the
|
||||
* kind of thing a field value hides and a sweep surfaces.
|
||||
*
|
||||
* ★ THE MODEL, grounded not remembered: capability = 5 - PROFILE_TIER, where PROFILE_TIER lives in
|
||||
* internal/plugin-gallery/discover.mjs:
|
||||
* nested-mfe-spa 0 · nested-elements-mfe 1 · monolithic 2 · parameterized 3 · single 4
|
||||
* One representative bundle per level. If a bundle's declared type changes, update REPS here.
|
||||
*
|
||||
* ★ TWO MEASUREMENT TRAPS THIS ENCODES (both cost real debugging time):
|
||||
* - SHADOW DOM DOES NOT PIERCE. A cap-4/cap-5 composite mounts CHILD elements with their OWN shadow
|
||||
* roots, so a single-level querySelector reports a working canvas as absent. Hence the recursive
|
||||
* deep() walk, and hence "a canvas that HOLDS a GL context" rather than "a canvas exists".
|
||||
* - shadowRoot.textContent INCLUDES the injected <style>, so regexing it matches CSS. Clone the
|
||||
* children, drop STYLE nodes, then read visible copy.
|
||||
*
|
||||
* WHAT COUNTS AS PASS: the element mounts, has real height (>=200px), renders real text (>40 chars),
|
||||
* and produces ZERO console errors and ZERO HTTP>=400. Rendering without errors is the bar, because a
|
||||
* console error is what masks the NEXT real failure.
|
||||
*
|
||||
* USAGE
|
||||
* bun run verify:5-caps # against the live catalog
|
||||
* BASE=http://127.0.0.1:8080/doxa-maps/ node internal/verify-5-capabilities.mjs # a local serve
|
||||
* Exits non-zero if any level fails, so it can gate a release.
|
||||
*/
|
||||
import { chromium } from 'playwright'
|
||||
|
||||
const GL=['--use-gl=swiftshader','--enable-unsafe-swiftshader','--ignore-gpu-blocklist','--enable-webgl','--no-sandbox']
|
||||
const B=(process.env.BASE || 'https://nuxtbase.app/plugin-assets/doxa-maps/').replace(/\/?$/,'/')
|
||||
// capability = 5 - PROFILE_TIER (discover.mjs). One representative per level.
|
||||
const REPS=[
|
||||
['cap-1 single', 'doxa-vision-card/doxa-vision-card/index.html', 'single'],
|
||||
['cap-2 parameterized', 'people-groups-grid/people-groups-grid/index.html', 'preset-element'],
|
||||
['cap-3 monolithic', 'doxa-research-map/research-map/index.html', 'monolithic'],
|
||||
['cap-4 nested-elements', 'dashboard-plus-grid/dashboard-plus-grid/index.html','nested-elements-mfe'],
|
||||
['cap-5 nested-mfe-spa', 'doxa-maps-spa/doxa-maps-spa/index.html', 'nested-mfe-spa'],
|
||||
]
|
||||
const b=await chromium.launch({headless:true,args:GL})
|
||||
let bad=0
|
||||
for(const [name,path,type] of REPS){
|
||||
const page=await b.newPage({viewport:{width:1440,height:900}})
|
||||
const errs=[]; page.on('console',m=>{if(m.type()==='error')errs.push(m.text().slice(0,60))})
|
||||
const http=[]; page.on('response',r=>{if(r.status()>=400)http.push(r.status()+' '+r.url().slice(-44))})
|
||||
try{ await page.goto(B+path+'?cb=5c',{waitUntil:'networkidle',timeout:90000}) }
|
||||
catch(e){ console.log(` FAIL ${name} — nav`); bad++; await page.close(); continue }
|
||||
await page.waitForTimeout(15000)
|
||||
const r=await page.evaluate(()=>{
|
||||
// shadow DOM does NOT pierce — walk nested roots
|
||||
const deep=(root,sel,out=[])=>{out.push(...root.querySelectorAll(sel))
|
||||
for(const el of root.querySelectorAll('*')) if(el.shadowRoot) deep(el.shadowRoot,sel,out); return out}
|
||||
const host=[...document.querySelectorAll('*')].find(e=>e.tagName.includes('-')&&e.shadowRoot)
|
||||
if(!host) return {mounted:false}
|
||||
const sr=host.shadowRoot, bb=host.getBoundingClientRect()
|
||||
// visible copy only (textContent includes injected <style>)
|
||||
const clone=document.createElement('div')
|
||||
;[...sr.children].forEach(c=>{if(c.tagName!=='STYLE')clone.appendChild(c.cloneNode(true))})
|
||||
clone.querySelectorAll('style').forEach(s=>s.remove())
|
||||
const txt=clone.textContent.replace(/\s+/g,' ').trim()
|
||||
const cv=deep(sr,'canvas').find(c=>c.getContext('webgl2')||c.getContext('webgl'))
|
||||
return { mounted:true, tag:host.tagName.toLowerCase(),
|
||||
size:Math.round(bb.width)+'x'+Math.round(bb.height),
|
||||
h:Math.round(bb.height),
|
||||
nested: [...sr.querySelectorAll('*')].filter(e=>e.tagName.includes('-')).length,
|
||||
glCanvas: !!cv, textLen: txt.length, head: txt.slice(0,58) }
|
||||
})
|
||||
const ok = r.mounted && r.h>=200 && r.textLen>40 && errs.length===0 && http.length===0
|
||||
console.log(` ${ok?'PASS':'FAIL'} ${name} [${type}]`)
|
||||
console.log(` ${r.mounted?`${r.tag} ${r.size} nested=${r.nested} gl=${r.glCanvas} text=${r.textLen}`:'DID NOT MOUNT'}`)
|
||||
if(r.head) console.log(` "${r.head}"`)
|
||||
if(errs.length) console.log(` console errors: ${errs.length} -> ${errs[0]}`)
|
||||
if(http.length) console.log(` HTTP>=400: ${http.length} -> ${http[0]}`)
|
||||
if(!ok) bad++
|
||||
await page.close()
|
||||
}
|
||||
console.log(`\n5-CAPS: ${bad?`FAIL — ${bad} of ${REPS.length}`:`PASS — all ${REPS.length} capability levels mount, render and are error-free`}`)
|
||||
await b.close()
|
||||
if (bad) process.exit(1)
|
||||
|
|
@ -46,7 +46,8 @@
|
|||
"check:template-literal": "node internal/template-literal-check.mjs",
|
||||
"verify:map-paint": "node internal/verify-map-paint.mjs",
|
||||
"check:sri": "node internal/sri-consistency-check.mjs",
|
||||
"check:settled": "node internal/settled-tree-gate.mjs"
|
||||
"check:settled": "node internal/settled-tree-gate.mjs",
|
||||
"verify:5-caps": "node internal/verify-5-capabilities.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@capacitor/android": "7",
|
||||
|
|
|
|||
Loading…
Reference in a new issue