import test from 'node:test';import assert from 'node:assert/strict';import fs from 'node:fs';import {loadSelectionTitle,paintSelectionTitle,titleLayout} from './src/selection-title.mjs';import {chapterLayout} from './src/level-select.mjs';import {boot} from './qa/runtime-harness.mjs'; test('title artwork is a transparent, high resolution PNG packaged locally',()=>{const b=fs.readFileSync(new URL('./images/title-reference.png',import.meta.url));assert.equal(b.subarray(1,4).toString(),'PNG');assert.equal(b.readUInt32BE(16),2132);assert.equal(b.readUInt32BE(20),737);assert.equal(b[25],6);}); test('asynchronous native image load requests a repaint and draws the artwork without a font substitute',()=>{const image={width:2132,height:737},draws=[];let invalidations=0;loadSelectionTitle({createImage:()=>image},()=>invalidations++);assert.equal(image.src,'images/title-reference.png');paintSelectionTitle({drawImage:(...args)=>draws.push(args)},375,92);assert.equal(draws.length,0);image.onload();assert.equal(invalidations,1);paintSelectionTitle({drawImage:(...args)=>draws.push(args)},375,92);assert.equal(draws[0][0],image);assert.equal(draws[0][3],224);assert.ok(Math.abs(draws[0][4]/draws[0][3]-737/2132)<1e-10);}); test('artwork stays above progress pills and clear of settings; fifteen cards still fit all phone sizes',()=>{for(const [width,height]of [[320,568],[375,667],[390,844],[414,896]]){const b=titleLayout(width,92),grid=chapterLayout(width,height,92,34);assert.ok(b.y+b.w*737/2132=16&&b.x+b.w<=width-16);assert.ok(b.pillY+24=44&&grid.w>=44);const ui=boot(new Map(),width,height);ui.select(3);ui.button('选关');const logo=ui.images.find(i=>i.source==='images/title-reference.png');assert.ok(logo);assert.ok(logo.w>180);assert.equal(ui.api.buttons().filter(b=>/^\d+( ✓)?$/.test(b.label)).length,15);}});