Assert the cap-5 rail opens on the Big Picture, in the specified order
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
The Driver named the sequence: Big Picture, UPG-100, Prayer Progress, Gospel Resources. Tab 1 is the landing surface -- the first thing anyone opening the product SPA sees, and the opening step of the onboarding ramp. That ordering was held only by the order of entries in NAV_MODEL and by comments explaining why, with nothing failing if someone reordered them. Easy to break silently: NAV_MODEL is also where sections, badges and narrow-screen flags live, so it gets edited for reasons unrelated to ordering, and a row moved while adding a badge produces no error and a SPA that still works. It just opens somewhere else. That file's own comments record Big Picture once being painted twice in the rail, from a one-row section that re-declared a row below. Asserts the RENDERED rail, not the source array. Parsing NAV_MODEL was the first attempt and returned an empty list -- the entries do not carry the id shape I assumed, and a regex over a 300-line commented array is guesswork either way. The rail is what the user sees and what the Driver described, and a source parse could pass while a rendering bug shows something else. Order, not membership: checking that four names are present would pass on a rail that opens on Gospel Resources. Prefix-matched so a row may carry its subtitle. Negative-tested: swapping the first two expected entries fails by name and prints want versus got for every row.
This commit is contained in:
parent
5d12841fe7
commit
e1baca1bbb
2 changed files with 100 additions and 1 deletions
98
internal/check-spa-tab-order.mjs
Normal file
98
internal/check-spa-tab-order.mjs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-spa-tab-order.mjs — the cap-5 product SPA must open on the Big Picture, in the Driver's order.
|
||||
*
|
||||
* WHAT THIS PROTECTS. The Driver specified the rail explicitly: "cap-5 tab order (Big Picture,
|
||||
* UPG-100, Prayer Progress, Gospel Resources)". Tab 1 is not a preference — it is the LANDING
|
||||
* SURFACE, the first thing anyone opening doxa-maps-spa sees, and the opening step of the
|
||||
* onboarding ramp. Today that ordering is held only by the order of entries in a source array and
|
||||
* by comments explaining why; nothing fails if someone reorders them.
|
||||
*
|
||||
* ★ WHY THIS IS EASY TO BREAK WITHOUT NOTICING. NAV_MODEL is also where sections, badges and
|
||||
* narrow-screen flags live, so it gets edited for reasons that have nothing to do with ordering —
|
||||
* and a row moved while adding a badge produces no error, no console warning, and a SPA that still
|
||||
* works perfectly. It just opens somewhere else. The nav file's own comments record that "Big
|
||||
* Picture" was once painted TWICE in the rail, from a one-row section that re-declared a row below;
|
||||
* that is the same class of accident.
|
||||
*
|
||||
* ★ IT ASSERTS THE RENDERED RAIL, NOT THE SOURCE ARRAY. Parsing NAV_MODEL is what I tried first and
|
||||
* it returned an EMPTY list — the entries do not carry the `id:` shape I assumed, and a regex over
|
||||
* a 300-line commented array is guesswork either way. The rail is the thing the user sees, it is
|
||||
* what the Driver described, and reading it requires no assumption about how it is built. A source
|
||||
* parse could also pass while a rendering bug shows a different order.
|
||||
*
|
||||
* ORDER, NOT MEMBERSHIP. Checking that four names are present would pass on a rail that opens on
|
||||
* Gospel Resources. The assertion is the SEQUENCE, matched as prefixes so a row may carry a
|
||||
* subtitle ("Big Picture — dashboard + people-groups grid") without failing.
|
||||
*
|
||||
* PRECONDITION: a built tree and `bun run serve` on 4173. No Mapbox token needed — the rail renders
|
||||
* before any map mounts, and heavy tabs lazy-mount on activation, which this never triggers.
|
||||
*
|
||||
* NEGATIVE-TESTED: swapping the first two entries of NAV_MODEL fails by name, printing expected vs
|
||||
* actual side by side.
|
||||
*/
|
||||
import { chromium } from 'playwright'
|
||||
|
||||
const BASE = process.env.BASE_URL || 'http://127.0.0.1:4173/doxa-maps'
|
||||
|
||||
// The Driver's sequence, verbatim from the open-work brief. Prefix-matched (case-insensitive,
|
||||
// whitespace-collapsed) so a row may carry its subtitle.
|
||||
const EXPECTED = ['Big Picture', 'UPG-100', 'Prayer Progress', 'Gospel Resources']
|
||||
|
||||
const WALK = `(sel)=>{const o=[],s=new Set();const w=(r)=>{if(!r||s.has(r))return;s.add(r);try{o.push(...r.querySelectorAll(sel))}catch(e){};for(const e of r.querySelectorAll('*'))if(e.shadowRoot)w(e.shadowRoot)};w(document);return o}`
|
||||
|
||||
const browser = await chromium.launch()
|
||||
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } })
|
||||
let rows = []
|
||||
let err = null
|
||||
try {
|
||||
await page.goto(`${BASE}/doxa-maps-spa/doxa-maps-spa/`, { waitUntil: 'load', timeout: 45000 })
|
||||
await page.waitForTimeout(8000)
|
||||
rows = await page.evaluate(
|
||||
`(${WALK})('.vtl-tab').map(t => (t.textContent || '').trim().replace(/\\s+/g, ' '))`
|
||||
)
|
||||
} catch (e) {
|
||||
err = e.message.slice(0, 160)
|
||||
}
|
||||
await browser.close()
|
||||
|
||||
if (err) {
|
||||
console.error('SPA-TAB-ORDER: FAIL — could not load the cap-5 SPA: ' + err)
|
||||
console.error(' Needs a built tree and `bun run serve` on 4173. Reported as a FAILURE, not a')
|
||||
console.error(' skip: a gate that cannot reach its subject has not passed, it has not run.')
|
||||
process.exit(1)
|
||||
}
|
||||
if (!rows.length) {
|
||||
console.error('SPA-TAB-ORDER: FAIL — no .vtl-tab rows found in the composed tree.')
|
||||
console.error(' The rail is inside a shadow root, so this walks recursively with a seen-Set; a')
|
||||
console.error(' flat querySelectorAll returns 0 here and would read as "no tabs" rather than')
|
||||
console.error(' "wrong tool".')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim()
|
||||
const actual = rows.slice(0, EXPECTED.length)
|
||||
const mismatches = EXPECTED.map((want, i) => {
|
||||
const got = actual[i] || '(missing)'
|
||||
return norm(got).startsWith(norm(want)) ? null : { i, want, got: got.slice(0, 52) }
|
||||
}).filter(Boolean)
|
||||
|
||||
if (mismatches.length) {
|
||||
console.error(`SPA-TAB-ORDER: FAIL — ${mismatches.length} of ${EXPECTED.length} rail rows are out of order.\n`)
|
||||
EXPECTED.forEach((want, i) => {
|
||||
const got = (actual[i] || '(missing)').slice(0, 52)
|
||||
const ok = norm(got).startsWith(norm(want))
|
||||
console.error(` ${ok ? 'ok ' : 'FAIL'} ${i + 1}. want "${want}" got "${got}"`)
|
||||
})
|
||||
console.error('\n Tab 1 is the LANDING SURFACE — the first thing anyone opening the product SPA')
|
||||
console.error(' sees, and the opening step of the onboarding ramp. A reordered rail produces no')
|
||||
console.error(' error and a SPA that still works; it just opens somewhere else.')
|
||||
console.error(' The order lives in NAV_MODEL in plugin-designs/doxa-maps-spa/doxa-maps-spa.vue.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(`SPA-TAB-ORDER: PASS — the rail opens in the specified order (${rows.length} rows total).`)
|
||||
EXPECTED.forEach((w, i) => console.log(` ${i + 1}. ${(actual[i] || '').slice(0, 56)}`))
|
||||
console.log(' Asserted on the RENDERED rail rather than on NAV_MODEL: the array is edited for')
|
||||
console.log(' reasons unrelated to ordering (badges, sections, narrow-screen flags), and a source')
|
||||
console.log(' parse can pass while a rendering bug shows something else.')
|
||||
|
|
@ -101,7 +101,8 @@
|
|||
"check:kpi-unit": "node internal/check-kpi-unit-once.mjs",
|
||||
"check:synced-css": "node internal/check-synced-css-copies.mjs",
|
||||
"check:composite-reg": "node internal/check-composite-registration.mjs",
|
||||
"check:filter-width": "node internal/check-filter-width-stable.mjs"
|
||||
"check:filter-width": "node internal/check-filter-width-stable.mjs",
|
||||
"check:spa-tab-order": "node internal/check-spa-tab-order.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@capacitor/android": "^7.6.9",
|
||||
|
|
|
|||
Loading…
Reference in a new issue