/** * The interface on a screen scaled to 150 %: the one thing every other step is blind to, since the * harness runs at 100 %, where a layout pixel is a device pixel. Text has to come out as hard as it * does at 100 %, and a hairline has to be one device pixel of its own colour, not one and a half * with the half mixed. Reloads the page twice, so the steps after it start from a fresh editor. */ export async function devicePixels(t) { const { check, json, cdp, navigate, select, storedObjects, wheel, exceptions } = t; const size = { width: await json('innerWidth'), height: await json('innerHeight') }; const scale = (deviceScaleFactor) => cdp.send('Emulation.setDeviceMetricsOverride', { ...size, deviceScaleFactor, mobile: false }); // The toolbar: captions on a plain ground, read at 100 % and at 150 %. const textEdges = () => json(`__gm.edgeContrast(0, 0, innerWidth, 30)`); const pickFirst = async () => { const holderId = await json('__gm.state.activeScene.id'); const first = (await storedObjects())[0]; await select({ kind: 'object', holderId, objectId: first.id, objectIds: [first.id] }); }; await pickFirst(); const plain = { grid: await json('__gm.pixelGrid()'), edges: await textEdges() }; await scale(1.5); await navigate(); await pickFirst(); // 62a. The editor knows the grid it draws on. const grid = await json('__gm.pixelGrid()'); check( 'at 150 % a hairline is one device pixel wide', plain.grid.resolution === 1 && plain.grid.hairline === 1 && grid.resolution === 1.5 && Math.abs(grid.hairline * 1.5 - 1) < 1e-9, JSON.stringify({ plain: plain.grid, grid }), ); // 62b. Both lines of every band of the inspector, read down a column of device pixels: one row // each, in the border's own colour, and only the band's colour between them. A band on an odd // layout pixel starts half-way between two device pixels, which is the case that used to blur. const colours = { border: await json(`__gm.themeColor('border')`), header: await json(`__gm.themeColor('panelHeader')`), }; // One pixel of scroll flips every band between even and odd, so both are read. The window is made // too low for the inspector's rows first: one that shows them all has nothing to scroll. await cdp.send('Emulation.setDeviceMetricsOverride', { width: size.width, height: 420, deviceScaleFactor: 1.5, mobile: false, }); await t.settle(); const inspector = await json('__gm.inspectorRect()'); const bands = []; for (const scrolled of [0, 1]) { if (scrolled) await wheel(inspector.x + inspector.w / 2, inspector.y + inspector.h / 2, 1); for (const title of ['SWITCHES', 'POSITION', 'TRANSFORM', 'PROPERTIES']) { const band = await json(`__gm.section(${JSON.stringify(title)})`); if (!band) continue; const { x, y, w, h } = band.rect; // A band cut off by the edge of the panel has no second line to read. if (y < inspector.y || y + h > inspector.y + inspector.h) continue; const column = await json(`__gm.deviceColumn(${x + w - 40}, ${y}, ${h})`); const runs = []; for (const colour of column) { if (runs.at(-1)?.colour === colour) runs.at(-1).rows += 1; else runs.push({ colour, rows: 1 }); } bands.push({ title, y, runs: runs.map((run) => `${run.colour}*${run.rows}`) }); } } const crisp = (band) => band.runs.length === 3 && band.runs[0] === `${colours.border}*1` && band.runs[1].startsWith(`${colours.header}*`) && band.runs[2] === `${colours.border}*1`; const odd = (band) => Math.round(band.y) % 2 === 1; check( 'the lines of a band are one device pixel each, in the unmixed colour', bands.every(crisp) && bands.some(odd) && bands.some((band) => !odd(band)), JSON.stringify({ bands, colours }), ); await scale(1.5); await t.settle(); // 62c. Text keeps its edges: against the same captions at 100 %, where nothing can sit between pixels. const edges = await textEdges(); const kept = edges / plain.edges; check( 'text is as hard-edged at 150 % as at 100 %', kept > 0.96, JSON.stringify({ plain: plain.edges, edges, kept }), ); await t.shot('62-device-pixels'); await scale(1); await navigate(); const back = await json('__gm.pixelGrid()'); check( 'back at 100 % without exceptions', back.resolution === 1 && exceptions().length === 0, JSON.stringify({ back, exceptions: exceptions() }), ); }