Files
universe/e2e/branded/matrix-reporter.ts
zeekayandHanzo Dev 6efce1c2d2 test(e2e): branded web3-surface matrix — live · functional · branded @ 4 breakpoints
Durable regression suite proving every public surface across Lux/Zoo/Hanzo/Pars
(explorers, wallets, id, votes, work boards, bridges, safe, clouds, exchanges)
is live, functional, and correctly branded at 390/768/1280/1920 — real
Chrome/Blink (channel:'chrome'), read-only (no txns, no wallet-connect, no auth;
the login form is asserted, never submitted).

- e2e/branded/surfaces.ts   — ONE source-of-truth table (surface × posture × flags);
                              breakpoint-scoped flags carry a reason + flip condition
- e2e/branded/branded.spec.ts — table-driven 8-check battery (the owner's 6-point
                              rubric: 200 · branded head + zero cross-brand leak ·
                              no overflow · no app-console-errors · key control
                              visible · brand font); gate vs flag vs stale-flag
- e2e/branded/matrix-reporter.ts — renders the surface × breakpoint PASS/FLAG/FAIL
                              grid + defect ledger; writes branded-report/matrix.{md,txt,json}
- playwright.branded.config.ts — breakpoints as projects, chrome channel, matrix reporter
- e2e.yml `branded` job + README + LLM.md section

Baseline 104 cells: 67 pass · 37 flagged (each with a flip condition) · 0
regressions. Only a NEW un-flagged failure turns the run red. Screenshots →
~/Desktop/lux-shots-e2e/. Bumps @luxfi/universe 1.0.1 → 1.0.2.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-26 15:46:04 -07:00

176 lines
7.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Reporter, TestCase, TestResult, FullResult } from '@playwright/test/reporter'
import { mkdirSync, writeFileSync } from 'fs'
import { join } from 'path'
import { SURFACES, BREAKPOINTS } from './surfaces'
// ---------------------------------------------------------------------------
// Matrix reporter — renders the branded regression as a surface × breakpoint
// PASS / FLAG / FAIL grid, plus a defect ledger (reason + flip per flag) and a
// stale-flag list so the table self-cleans. Prints to console and writes
// branded-report/{matrix.md,matrix.json}.
// ---------------------------------------------------------------------------
type Cell = {
cellState: 'PASS' | 'FLAG' | 'FAIL'
checks: { name: string; ok: boolean; detail: string }[]
flagged: { check: string; reason: string; flip: string; detail: string }[]
unexpected: { name: string; detail: string }[]
staleFlags: string[]
accentNote: string
bodyForeign: string[]
posture: string
brand: string
url: string
}
const BP_NAMES = BREAKPOINTS.map((b) => b.name)
const GLYPH: Record<string, string> = { PASS: ' ✓ ', FLAG: ' ⚑ ', FAIL: ' ✗ ', '': ' · ' }
export default class MatrixReporter implements Reporter {
private cells = new Map<string, Cell>() // key = `${id}::${bp}`
private outDir = join(process.cwd(), 'branded-report')
onBegin() {
mkdirSync(this.outDir, { recursive: true })
}
onTestEnd(test: TestCase, result: TestResult) {
const att = result.attachments.find((a) => a.name === 'branded-result' && a.body)
if (!att || !att.body) {
// Crashed/timed out before attaching — synthesize a FAIL cell keyed by title+project.
const bp = test.parent?.project()?.name || test.titlePath()[1] || '?'
this.cells.set(`${test.title}::${bp}`, {
cellState: 'FAIL', checks: [], flagged: [],
unexpected: [{ name: 'run', detail: `${result.status} (no result attached)` }],
staleFlags: [], accentNote: '', bodyForeign: [], posture: '', brand: '', url: '',
})
return
}
const d = JSON.parse(att.body.toString())
this.cells.set(`${d.id}::${d.bp}`, d)
}
onEnd(result: FullResult) {
const lines: string[] = []
const w = 15
const pad = (s: string, n: number) => (s + ' '.repeat(n)).slice(0, n)
// ── Header ──
lines.push('')
lines.push('═'.repeat(72))
lines.push(' BRANDED WEB3-SURFACE MATRIX — live · functional · branded @ 4 breakpoints')
lines.push(' (real Chrome/Blink · read-only · ✓ pass ⚑ flagged/expected ✗ regression)')
lines.push('═'.repeat(72))
lines.push('')
lines.push(pad(' surface', w + 9) + BP_NAMES.map((b) => pad(' ' + b, 6)).join('') + ' brand')
lines.push(' ' + '─'.repeat(w + 7 + BP_NAMES.length * 6 + 8))
let pass = 0, flag = 0, fail = 0
let lastCat = ''
const ledger: string[] = []
const stales: string[] = []
for (const s of SURFACES) {
if (s.category !== lastCat) { lines.push(` ${s.category.toUpperCase()}`); lastCat = s.category }
const row = [pad(' ' + s.id, w + 9)]
for (const bp of BP_NAMES) {
const c = this.cells.get(`${s.id}::${bp}`)
const state = c?.cellState || ''
row.push(pad(GLYPH[state] || GLYPH[''], 6))
if (state === 'PASS') pass++; else if (state === 'FLAG') flag++; else if (state === 'FAIL') fail++
}
// Use the 1280 cell (or any) for the per-surface annotation summary.
const rep = this.cells.get(`${s.id}::1280`) || this.cells.get(`${s.id}::${BP_NAMES[0]}`)
row.push(' ' + s.brand)
lines.push(row.join(''))
if (rep) {
for (const f of dedupe(rep.flagged, (x) => x.check)) {
ledger.push(` ⚑ ${pad(s.id, w)} ${f.check.padEnd(8)} ${f.reason}`)
if (f.flip) ledger.push(` ${' '.repeat(w + 12)}↳ FLIP: ${f.flip}`)
}
for (const u of dedupe(rep.unexpected, (x) => x.name)) {
ledger.push(` ✗ ${pad(s.id, w)} ${u.name.padEnd(8)} REGRESSION — ${u.detail}`)
}
if (rep.staleFlags?.length) stales.push(` ${pad(s.id, w)} ${rep.staleFlags.join(', ')} — now passing; delete the flag in surfaces.ts`)
}
}
// ── Summary ──
const total = pass + flag + fail
lines.push('')
lines.push(' ' + '─'.repeat(w + 7 + BP_NAMES.length * 6 + 8))
lines.push(` cells: ${total} ✓ ${pass} pass ⚑ ${flag} flagged ✗ ${fail} regressions`)
lines.push('')
if (ledger.length) {
lines.push(' DEFECT LEDGER (⚑ documented + flip condition · ✗ new regression)')
lines.push(' ' + '─'.repeat(68))
lines.push(...ledger)
lines.push('')
}
if (stales.length) {
lines.push(' STALE FLAGS (a documented defect is now passing — clean up surfaces.ts)')
lines.push(' ' + '─'.repeat(68))
lines.push(...stales)
lines.push('')
}
// ── Accent notes (observational) ──
lines.push(' ACCENT / FONT NOTES (observational)')
lines.push(' ' + '─'.repeat(68))
for (const s of SURFACES) {
const rep = this.cells.get(`${s.id}::1280`) || this.cells.get(`${s.id}::${BP_NAMES[0]}`)
if (rep?.accentNote) {
const fontChk = rep.checks.find((c) => c.name === 'font')
lines.push(` ${pad(s.id, w)} ${rep.accentNote}${fontChk ? ` · font: ${fontChk.detail}` : ''}${rep.bodyForeign?.length ? ` · body-ref: ${rep.bodyForeign.join(',')}` : ''}`)
}
}
lines.push('')
lines.push(` overall playwright status: ${result.status}`)
lines.push('═'.repeat(72))
lines.push('')
const text = lines.join('\n')
console.log(text)
// ── Persist artifacts ──
const md = this.toMarkdown(pass, flag, fail, ledger, stales)
writeFileSync(join(this.outDir, 'matrix.txt'), text)
writeFileSync(join(this.outDir, 'matrix.md'), md)
writeFileSync(join(this.outDir, 'matrix.json'), JSON.stringify({
generatedAt: new Date().toISOString(),
summary: { total, pass, flag, fail, status: result.status },
cells: Object.fromEntries(this.cells),
}, null, 2))
console.log(` matrix written → ${join(this.outDir, 'matrix.md')}`)
}
private toMarkdown(pass: number, flag: number, fail: number, ledger: string[], stales: string[]): string {
const out: string[] = []
out.push('# Branded web3-surface matrix', '')
out.push('Live · functional · branded verification at 4 breakpoints (390 / 768 / 1280 / 1920), real Chrome/Blink, read-only.', '')
out.push(`**${pass} pass · ${flag} flagged · ${fail} regressions**`, '')
out.push('Legend: ✓ pass · ⚑ documented flag (expected, carries a flip condition) · ✗ regression (un-flagged failure) · · not run', '')
out.push('| surface | brand | ' + BP_NAMES.join(' | ') + ' |')
out.push('|---|---|' + BP_NAMES.map(() => '---').join('|') + '|')
let lastCat = ''
for (const s of SURFACES) {
if (s.category !== lastCat) { out.push(`| **${s.category}** | | ${BP_NAMES.map(() => '').join(' | ')} |`); lastCat = s.category }
const cells = BP_NAMES.map((bp) => {
const st = this.cells.get(`${s.id}::${bp}`)?.cellState || ''
return st === 'PASS' ? '✓' : st === 'FLAG' ? '⚑' : st === 'FAIL' ? '✗' : '·'
})
out.push(`| ${s.id} | ${s.brand} | ${cells.join(' | ')} |`)
}
out.push('')
if (ledger.length) { out.push('## Defect ledger', '', '```', ...ledger, '```', '') }
if (stales.length) { out.push('## Stale flags (now passing — clean up `surfaces.ts`)', '', '```', ...stales, '```', '') }
return out.join('\n')
}
}
function dedupe<T>(arr: T[], key: (x: T) => string): T[] {
const seen = new Set<string>()
return arr.filter((x) => { const k = key(x); if (seen.has(k)) return false; seen.add(k); return true })
}