From 0289c28ea8cd98e2be41f220f16a0d2c2e6aa5de Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 5 Jul 2026 17:22:29 +0900 Subject: [PATCH] Verify public canvas pixels --- scripts/verify-public-deploy.mjs | 38 +++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/scripts/verify-public-deploy.mjs b/scripts/verify-public-deploy.mjs index f8149f2..b2b21eb 100644 --- a/scripts/verify-public-deploy.mjs +++ b/scripts/verify-public-deploy.mjs @@ -48,15 +48,51 @@ try { width: canvas.width, height: canvas.height, clientWidth: canvas.clientWidth, - clientHeight: canvas.clientHeight + clientHeight: canvas.clientHeight, + pixelProbe: sampleCanvasPixels(canvas) } : undefined }; + + function sampleCanvasPixels(canvas) { + const context = canvas.getContext('2d', { willReadFrequently: true }); + if (!context) { + return { readable: false, sampleCount: 0, nonTransparentRatio: 0, distinctColorBuckets: 0 }; + } + + const stepX = Math.max(1, Math.floor(canvas.width / 64)); + const stepY = Math.max(1, Math.floor(canvas.height / 64)); + const colorBuckets = new Set(); + let sampleCount = 0; + let nonTransparentCount = 0; + + for (let y = Math.floor(stepY / 2); y < canvas.height; y += stepY) { + for (let x = Math.floor(stepX / 2); x < canvas.width; x += stepX) { + const [r, g, b, a] = context.getImageData(x, y, 1, 1).data; + sampleCount += 1; + if (a > 8) { + nonTransparentCount += 1; + } + colorBuckets.add(`${r >> 4},${g >> 4},${b >> 4},${a >> 5}`); + } + } + + return { + readable: true, + sampleCount, + nonTransparentRatio: sampleCount > 0 ? nonTransparentCount / sampleCount : 0, + distinctColorBuckets: colorBuckets.size + }; + } }); assert(state.title?.trim().length > 0, `Expected deployed page title: ${JSON.stringify(state)}`); assert(state.canvasCount === 1, `Expected exactly one game canvas: ${JSON.stringify(state)}`); assert(state.canvas?.width >= 960 && state.canvas?.height >= 540, `Expected desktop-size game canvas: ${JSON.stringify(state)}`); + assert(state.canvas?.pixelProbe?.readable === true, `Expected readable canvas pixels: ${JSON.stringify(state)}`); + assert(state.canvas.pixelProbe.sampleCount >= 500, `Expected enough canvas pixel samples: ${JSON.stringify(state)}`); + assert(state.canvas.pixelProbe.nonTransparentRatio > 0.5, `Expected non-empty canvas pixels: ${JSON.stringify(state)}`); + assert(state.canvas.pixelProbe.distinctColorBuckets >= 12, `Expected visually varied canvas pixels: ${JSON.stringify(state)}`); const relevantLogs = consoleMessages.filter( (message) => message.type === 'error' || (isWarning(message.type) && relevantLogPattern.test(message.text))