- commit
- f5b5ce820a89ca4d10c8d2ed86504c4673ddaf65
- parent
- 76f7a79a973e2e965f5070db857eac503d160e02
- author
- Dami <dami.etoile@gmail.com>
| M | tests/ | 47 | +++++++++++++++++++++++++++++- |
@@ -241,6 +241,46 @@ async function assertFeedback(page, pattern, label) {
241241 report(label);242242 }243243 244+async function assertCanvasRound3Typography(page, { ledger = false, win = false } = {}) {245+ const checks = await page.evaluate(() => {246+ const canvas = document.querySelector('.stage-canvas');247+ const ctx = canvas?.getContext('2d');248+ if (!canvas || !ctx) return null;249+ const pixel = ctx.getImageData(0, 0, canvas.width, canvas.height).data;250+ const countLight = (left, top, width, height, threshold = 180) => {251+ let count = 0;252+ for (let y = top; y < top + height; y += 1) for (let x = left; x < left + width; x += 1) {253+ const offset = (y * canvas.width + x) * 4;254+ if (pixel[offset] > threshold && pixel[offset + 1] > threshold && pixel[offset + 2] > threshold) count += 1;255+ }256+ return count;257+ };258+ // The first ledger operator pill is stable at desktop proof geometry.259+ const ledgerOperatorPixels = countLight(365, 653, 22, 17);260+ // The first operator in the centred FLAG IS WIN sentence on the Win card.261+ const winOperatorPixels = countLight(620, 245, 55, 45);262+ const rows = [];263+ for (let y = 315; y < 420; y += 1) {264+ if (countLight(345, y, 590, 1, 70) > 2) rows.push(y);265+ }266+ const runs = [];267+ for (const row of rows) {268+ const prior = runs[runs.length - 1];269+ if (prior && row === prior[1] + 1) prior[1] = row;270+ else runs.push([row, row]);271+ }272+ return { ledgerOperatorPixels, winOperatorPixels, textRuns: runs };273+ });274+ assert.ok(checks, 'stage canvas should be readable for round-3 typography checks');275+ if (ledger) assert.ok(checks.ledgerOperatorPixels > 2, 'ledger IS pill should contain visible light operator text');276+ if (win) assert.ok(checks.winOperatorPixels > 2, 'Win card IS pill should contain visible light operator text');277+ if (!win) return;278+ const headline = checks.textRuns.find(run => run[0] >= 330 && run[0] <= 370);279+ const progress = checks.textRuns.find(run => run[0] > (headline?.[1] ?? 0) && run[0] < 405);280+ assert.ok(headline && progress, 'Win card should render headline and progress text bands');281+ assert.ok(progress[0] - headline[1] >= 10, 'Win card progress line should have visible spacing below headline');282+}283+244284 async function openDevProof(page) {245285 await page.keyboard.press('Enter');246286 await page.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');@@ -269,6 +309,7 @@ page.on('console', message => { if (message.type() === 'error') consoleErrors.pu
269309 try {270310 const stagePage = await context.newPage();271311 stagePage.setDefaultTimeout(timeout);312+ await stagePage.setViewportSize({ width: 1280, height: 800 });272313 const stageErrors = [];273314 stagePage.on('console', message => { if (message.type() === 'error') stageErrors.push(message.text()); });274315 stagePage.on('pageerror', error => stageErrors.push(error.message));@@ -311,7 +352,9 @@ try {
311352 report('INDEX Workshop footer pointer opens Workshop and Escape returns');312353 await stagePage.keyboard.press('Enter');313354 await stagePage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'proof');355+ await stagePage.waitForTimeout(700);314356 await stagePage.screenshot({ path: `${stageScreenshotDir}/proof.png` });357+ await assertCanvasRound3Typography(stagePage, { ledger: true });315358 const stageInitial = JSON.parse(await stagePage.evaluate(() => window.render_game_to_text()));316359 await stagePage.keyboard.press('ArrowRight');317360 await stagePage.waitForFunction(initial => JSON.parse(window.render_game_to_text()).moves > initial, stageInitial.moves);@@ -321,7 +364,9 @@ try {
321364 for (const key of ['ArrowRight', 'ArrowRight', 'ArrowUp', 'ArrowUp', ...Array(4).fill('ArrowDown'), ...Array(7).fill('ArrowRight')]) await stagePage.keyboard.press(key);322365 await stagePage.waitForFunction(() => JSON.parse(window.render_game_to_text()).mode === 'won');323366 await stagePage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'win');367+ await stagePage.waitForTimeout(700);324368 await stagePage.screenshot({ path: `${stageScreenshotDir}/win.png` });369+ await assertCanvasRound3Typography(stagePage, { win: true });325370 assert.equal(await stagePage.locator('.stage-shell').getAttribute('data-scene'), 'win', 'winning a proof should open the win scene');326371 const progressKeys = await stagePage.evaluate(() => Object.keys(localStorage).filter(key => key.startsWith('miau.index.progress/')));327372 assert.ok(progressKeys.some(key => key.includes('core/')), 'winning should persist core proof progress');@@ -734,7 +779,7 @@ try {
734779 const indexCanvas = accessibilityPage.locator('.stage-canvas');735780 const indexBox = await indexCanvas.boundingBox();736781 assert.ok(indexBox, 'mobile INDEX should expose a tappable canvas');737- await accessibilityPage.mouse.click(indexBox.x + 240, indexBox.y + 820);782+ await accessibilityPage.mouse.click(indexBox.x + 270, indexBox.y + 820);738783 await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'workshop');739784 await accessibilityPage.keyboard.press('Escape');740785 await accessibilityPage.waitForFunction(() => document.querySelector('.stage-shell')?.dataset.scene === 'index');