Files
universe/e2e/31-vote-surfaces.spec.ts
zeekayandHanzo Dev 66f8705e89 test(e2e): reflect live governance surfaces — Dework work-boards + hanzo.vote guard
32-work-boards: work.zoo + work.pars now both serve the redesigned Dework board
(luxfi/work-board). Replace the stale literal assertions (BountyV1 / #0 / "N
bounties" — removed by the redesign, the 3 false failures) with the real structural
contract via one parameterized factory: Dework chrome (icon-rail + w-64 <aside>
sidebar + Overview / Community Suggestions / Leaderboards / Combined Board nav),
inline-<svg> BrandMark (the logo is not an <img>), the Bounties Space-category grid,
the To Do / In Progress / In Review / Done kanban columns, the emerald LIVE badge
(not the amber FIXTURE fallback) + org chainId, and a UI<->chain check (the rendered
treasury truncation equals on-chain BountyV1.treasury()). Un-skip Pars (now live,
chain 494949); Lux/Hanzo stay skip-gated (HTTP-unreachable, brands.ts bounty==0x0).

31-vote: the vote shells are retiring -> work.<org>.network. Arm a hanzo.vote
shell-health guard that hammers cold loads and fails on the intermittent blank-#root
white-screen (Buffer-crash class) OR the "Made with love by Lux" cross-brand leak —
a real, known-flagged regression, kept armed (not muted/skipped). lux/zoo/pars green.

work-boards: 1 -> 10 passing (3 false failures fixed, Pars un-skipped, 2 skip).
vote: hanzo.vote deterministically flagged; lux/zoo/pars (6) green, 4 skip.

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

150 lines
7.6 KiB
TypeScript

import { test, expect, Page, Browser } from '@playwright/test'
// ---------------------------------------------------------------------------
// Vote surfaces — the 4 <org>.vote governance front-ends
// ---------------------------------------------------------------------------
// RETIRING → work.<org>.network. Owner direction: the vote shells (Decent-fork,
// reading ZERO chain) converge onto the WORK BOARD, which is the genuinely-live
// governance surface (see 32-work-boards). Until each shell is replaced, these stay
// as regression guards. For the three healthy shells (lux/zoo/pars) that means:
// • HTTP 200 + correct brand in <title> and rendered DOM.
// • ZERO cross-brand leakage — a surface must never render another org's brand
// (nor a forbidden competitor term). The white-label invariant.
// • NOT a white-screen — #root has real descendants and the body has real text.
// • The Connect Wallet control is present in the DOM.
// The "reads a real on-chain proposal/DAO tile" assertion stays SKIPPED (with the
// exact flip condition) so the spec is SAVED and ready the moment a surface goes live.
//
// hanzo.vote — ARMED shell-health guard, EXPECTED RED (real, flagged defects).
// hanzo.vote is a broken, retiring shell with TWO intermittent defects (both tied to
// an unstable Decent-fork render, verified 2026-07-17 — hanzo ~50% blank, lux/zoo/pars
// 0/N):
// 1. blank #root white-screen — HTTP 200 but an EMPTY #root, from
// `Cannot read properties of undefined (reading 'address')` after a 403 (the
// Buffer-crash class).
// 2. a "Made with love by Lux" footer — Lux's name on a Hanzo surface, a
// cross-brand white-label violation.
// A single load is a coin-flip, so the guard HAMMERS several fresh loads and fails on
// ANY white-screen or ANY cross-brand leak. It is NOT muted or skipped — it stays red
// until work.hanzo replaces the shell.
interface Surface {
org: string
brand: string
url: string
foreign: string[] // other org brands + competitor terms that must NEVER appear
}
const ORG_BRANDS = ['Lux', 'Zoo', 'Hanzo', 'Pars']
// Competitor / forbidden lineage terms (owner rules: no Avalanche, no "snowman").
const FORBIDDEN = ['Avalanche', 'Snowman', 'Ava Labs']
const SURFACES: Surface[] = ORG_BRANDS.map((brand) => ({
org: brand.toLowerCase(),
brand,
url: `https://${brand.toLowerCase()}.vote`,
foreign: [...ORG_BRANDS.filter((b) => b !== brand), ...FORBIDDEN],
}))
async function render(page: Page, url: string) {
const res = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45_000 })
await page.waitForTimeout(4000) // let the SPA hydrate
const title = await page.title()
const rootKids = await page.evaluate(() => {
const r = document.getElementById('root') || document.getElementById('app') || document.getElementById('__next')
return r ? r.querySelectorAll('*').length : -1
})
const bodyText = await page.evaluate(() => document.body.innerText || '')
return { status: res?.status() ?? 0, title, rootKids, bodyText }
}
// One cold load in a FRESH context (no cache/cookies) — how the blank-crash reproduces.
// Only HTTP 200 responses count toward the checks; a nav/infra failure (goto throws /
// non-200) is retried and is NOT the defect.
async function coldLoad(browser: Browser, url: string, foreign: string[]) {
for (let attempt = 0; attempt < 3; attempt++) {
const ctx = await browser.newContext()
const page = await ctx.newPage()
try {
const res = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 45_000 })
const status = res?.status() ?? 0
await page.waitForTimeout(4000)
const data = await page.evaluate((foreign) => {
const r = document.getElementById('root') || document.getElementById('app') || document.getElementById('__next')
const bt = document.body.innerText || ''
return {
rootKids: r ? r.querySelectorAll('*').length : -1,
bodyLen: bt.trim().length,
foreignHits: foreign.filter((t) => new RegExp(`\\b${t}\\b`, 'i').test(bt)),
}
}, foreign)
await ctx.close()
if (status !== 200) continue // infra hiccup, not the defect — retry
return { status, ...data }
} catch {
await ctx.close() // nav error — retry
}
}
return { status: 0, rootKids: -1, bodyLen: 0, foreignHits: [] as string[] }
}
for (const s of SURFACES) {
test.describe(`Vote Surface — ${s.brand} (${s.org}.vote)`, () => {
if (s.org === 'hanzo') {
// ARMED guard — expected RED. See file header. Do NOT mute or skip: a green
// result means hanzo.vote stopped blank-crashing AND leaking (or work.hanzo
// replaced it).
test('shell-health guard — no white-screen, no cross-brand leak (RETIRING → work.hanzo)', async ({ browser }) => {
const ATTEMPTS = 6
const whiteScreens: number[] = []
const leaks = new Set<string>()
for (let i = 0; i < ATTEMPTS; i++) {
const r = await coldLoad(browser, s.url, s.foreign)
const blank = r.status === 200 && (r.rootKids <= 20 || r.bodyLen < 100)
if (blank) whiteScreens.push(i + 1)
r.foreignHits.forEach((t) => leaks.add(t))
console.log(
` hanzo.vote load ${i + 1}/${ATTEMPTS}: HTTP ${r.status} · #root kids=${r.rootKids} · bodyLen=${r.bodyLen}` +
`${blank ? ' <== WHITE-SCREEN' : ''}${r.foreignHits.length ? ' <== LEAK: ' + r.foreignHits.join(',') : ''}`,
)
}
const defects: string[] = []
if (whiteScreens.length) defects.push(`white-screened on loads [${whiteScreens.join(',')}]/${ATTEMPTS} (blank #root — Buffer-crash class)`)
if (leaks.size) defects.push(`leaked foreign brand(s): ${[...leaks].join(', ')} (white-label violation)`)
expect(
defects,
`hanzo.vote is broken — ${defects.join(' · ')}. KNOWN-FLAGGED regression; retiring → work.hanzo.`,
).toEqual([])
})
} else {
test('renders with correct brand and is not a white-screen', async ({ page }) => {
const { status, title, rootKids, bodyText } = await render(page, s.url)
const clean = bodyText.replace(/\s+/g, ' ').trim()
console.log(` ${s.org}.vote: HTTP ${status} · title=${JSON.stringify(title)} · #root kids=${rootKids} · bodyLen=${clean.length}`)
expect(status).toBe(200)
// Brand in the document title AND in the rendered body.
expect(title).toMatch(new RegExp(s.brand, 'i'))
expect(title).toMatch(/DAO/i)
expect(clean).toMatch(new RegExp(`\\b${s.brand}\\b`, 'i'))
// Not a white-screen: real DOM under #root and real visible text.
expect(rootKids).toBeGreaterThan(20)
expect(clean.length).toBeGreaterThan(100)
// Connect Wallet control present.
await expect(page.getByText(/connect wallet/i).first()).toBeVisible()
})
test('has ZERO cross-brand / competitor leakage', async ({ page }) => {
const { bodyText } = await render(page, s.url)
const hits = s.foreign.filter((term) => new RegExp(`\\b${term}\\b`, 'i').test(bodyText))
console.log(` ${s.org}.vote foreign hits: ${hits.length ? hits.join(', ') : 'NONE'}`)
expect(hits, `${s.org}.vote leaked foreign brand(s): ${hits.join(', ')}`).toEqual([])
})
}
test('reads a live on-chain proposal / DAO tile', async () => {
test.skip(true, `live after CTO agent deploy — ${s.org}.vote is a Decent-fork shell reading zero chain (memory 2026-07-17); flip when it renders an on-chain proposal/DAO tile. Owner direction: converge to work.${s.org}.network.`)
})
})
}