Android: a Now Playing that survives a 439px screen, and the audit behind it #188

Merged
logan merged 7 commits from 51-android-small-screens into main 2026-08-21 20:19:46 +00:00
Showing only changes of commit ce9951b93a - Show all commits
+34 -18
View File
@@ -107,11 +107,19 @@ async function openNowPlaying(page: Page): Promise<void> {
const el = v?.shadowRoot?.querySelector('.art img, .art .placeholder'); const el = v?.shadowRoot?.querySelector('.art img, .art .placeholder');
const t = v?.shadowRoot?.querySelector('.transport'); const t = v?.shadowRoot?.querySelector('.transport');
if (!stack || !el || !t) return false; if (!el || !t) return false;
const dir = getComputedStyle(stack).flexDirection; // A build with no `.stack` at all is the one before this change,
// and the squareness assertions are still meaningful against it
// -- so this waits for the arrangement only where there is one to
// wait for. Otherwise reverting the component to check that these
// tests bite produces eight timeouts instead of the measurements
// that make the case.
if (stack) {
const dir = getComputedStyle(stack).flexDirection;
if (dir !== (row ? 'row' : 'column')) return false; if (dir !== (row ? 'row' : 'column')) return false;
}
const r = el.getBoundingClientRect(); const r = el.getBoundingClientRect();
@@ -152,6 +160,7 @@ async function boxes(page: Page) {
// Whichever of the two the track has; both carry the sizing. // Whichever of the two the track has; both carry the sizing.
art: rect('.art img') ?? rect('.art .placeholder'), art: rect('.art img') ?? rect('.art .placeholder'),
artBox: rect('.art'), artBox: rect('.art'),
stack: rect('.stack'),
meta: rect('.meta'), meta: rect('.meta'),
transport: rect('.transport'), transport: rect('.transport'),
scrollHeight: v.scrollHeight, scrollHeight: v.scrollHeight,
@@ -239,29 +248,36 @@ test.describe('Now Playing survives a short screen', () => {
}); });
/** /**
* The reflow is only worth having if it buys something, and the * What the reflow actually does, stated as a mechanism rather than
* honest form of that is a floor on the device's own viewport * as a number: in a row the art is bounded by the row's *height*,
* rather than a comparison with a layout that is no longer there. * so it fills it — where in a column it is the leftover after the
* names, which is what made it 53px.
* *
* The first draft compared the art against the column's leftover * **The pixel count is deliberately not asserted here.** Two drafts
* computed from the boxes on screen, and it passed on the build * tried. The first compared the art against the column's leftover
* before this change as well -- the subtraction goes negative when * computed from the boxes on screen and passed on the broken build,
* the names are taller than the art, which is precisely the broken * because the subtraction goes negative when the names are taller
* state. A test that cannot fail on the defect is not evidence. * than the art — precisely the defect. The second put a floor of
* 100px on it, passed locally at 114 and **failed in CI at 64**: this
* app is long-lived, so a job staged by an earlier spec is still on
* screen, and the volume control renders here where it does not on
* Android. Both are chrome above and below this view, and both move
* the leftover. A test that asserts how much room CI happened to
* have is a test about the runner.
* *
* 100 is a floor, not the measurement: the device draws 143 and CI's * The device numbers — 53px to 143px — are on #51, measured there,
* chrome differs by whatever the volume control adds, so pinning the * which is the only tier that can honestly produce them.
* exact number would make this a test about the runner.
*/ */
test('gives the art a real size on the reference device', async ({ app }) => { test('fills the row with the art rather than the leftover', async ({ app }) => {
await app.setViewportSize(DEVICE); await app.setViewportSize(DEVICE);
await openNowPlaying(app); await openNowPlaying(app);
const b = await boxes(app); const b = await boxes(app);
expect(b!.stack, 'there is no row to fill').not.toBeNull();
expect( expect(
b!.art!.height, Math.abs(b!.artBox!.height - b!.stack!.height),
'the art is still a sliver at the size #51 is about', 'the art does not fill the row, so it is still a leftover',
).toBeGreaterThan(100); ).toBeLessThanOrEqual(1);
}); });
}); });