feat(js): m11 pong capstone (model, rules, view, app, page) and polling smoke test

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-28 23:59:25 +01:00
co-authored by Claude Sonnet 5.5
parent fd7c5f2acf
commit 1d1d918805
16 changed files with 3090 additions and 3 deletions
+15 -3
View File
@@ -47,6 +47,7 @@ try {
const ws = new WebSocket(target.webSocketDebuggerUrl)
await new Promise((resolve, reject) => { ws.onopen = resolve; ws.onerror = reject })
const errors = []
let loaded = false
const pending = new Map()
let id = 0
const send = (method, params = {}) => new Promise((resolve) => {
@@ -56,6 +57,7 @@ try {
ws.onmessage = ({ data }) => {
const m = JSON.parse(data)
if (m.id && pending.has(m.id)) { pending.get(m.id)(m.result ?? m.error); pending.delete(m.id) }
else if (m.method === 'Page.loadEventFired') loaded = true
else if (m.method === 'Runtime.exceptionThrown') {
const d = m.params.exceptionDetails
errors.push(`exception: ${d.exception?.description ?? d.text}`)
@@ -69,7 +71,7 @@ try {
await send('Log.enable')
await send('Page.enable')
await send('Page.navigate', { url })
await sleep(1000)
for (let i = 0; i < 100 && !loaded; i++) await sleep(100) // wait for the page load event (up to 10s)
// Does the canvas hold at least one pixel that is not the #111111 background (and not transparent)?
const check = `(() => {
@@ -80,8 +82,18 @@ try {
if (d[i + 3] !== 0 && (d[i] !== 0x11 || d[i + 1] !== 0x11 || d[i + 2] !== 0x11)) return 'painted'
return 'blank'
})()`
const res = await send('Runtime.evaluate', { expression: check, returnByValue: true })
const state = res.result?.value ?? `evaluation failed: ${JSON.stringify(res)}`
// Poll instead of sleeping a fixed time: the first frame is drawn on the first animation frame after load.
const probe = async () => {
const res = await send('Runtime.evaluate', { expression: check, returnByValue: true })
return res.result?.value ?? `evaluation failed: ${JSON.stringify(res)}`
}
const poll = async (left) => {
const state = await probe()
if (state === 'painted' || left === 0) return state
await sleep(200)
return poll(left - 1)
}
const state = await poll(25) // every 200 ms, up to 5 s
const problems = [...(state === 'painted' ? [] : [`canvas: ${state}`]), ...errors]
clearTimeout(timer)