// SPDX-FileCopyrightText: 2026 Stan Grams // // SPDX-License-Identifier: GPL-2.0-or-later // What a phone gets. A page that scrolls sideways is a page with something on // it nobody will find: the transmit controls spent this whole period laid out // at x=400 on a 390 px screen, off the side of a tray 354 px wide, reachable // only by a horizontal scroll with nothing to say it was there. import assert from "node:assert/strict"; import { chromium } from "playwright-core"; import { startBrowser, startWebFixture } from "./web-fixture.mjs"; /* global document, getComputedStyle */ const PHONES = [430, 390, 360]; const fixture = await startWebFixture({ spectrum: true, tx: true }); const { browser, page, runtimeErrors } = await startBrowser(chromium); try { for (const width of PHONES) { await page.setViewportSize({ width, height: 900 }); await page.goto(fixture.origin, { waitUntil: "domcontentloaded" }); await page.locator("#content").waitFor({ state: "visible" }); await page.waitForTimeout(1800); const layout = await page.evaluate((viewport) => { const box = (element) => element.getBoundingClientRect(); // Anything laid out past the right edge of the screen. const past = []; const walk = (element) => { const rect = box(element); if (rect.width > 0 && rect.right > viewport + 1) { past.push(`${element.tagName.toLowerCase()}${element.id ? `#${element.id}` : ""}`); } for (const child of element.children) walk(child); }; walk(document.getElementById("content")); const tx = document.getElementById("tx-power-col"); return { documentScroll: document.documentElement.scrollWidth, past: past.slice(0, 6), txRight: tx ? Math.round(box(tx).right) : null, txVisible: tx ? getComputedStyle(tx).display !== "none" : false, rigSelect: Math.round(box(document.getElementById("header-rig-switch-select")).width), }; }, width); assert.equal(layout.documentScroll, width, `the page scrolls sideways at ${width}px (${layout.documentScroll}px wide)`); assert.deepEqual(layout.past, [], `laid out past the screen at ${width}px: ${layout.past.join(", ")}`); if (layout.txVisible) { assert.ok(layout.txRight <= width + 1, `the transmit controls end at ${layout.txRight}px on a ${width}px screen`); } // The rig's name was taking 139px of a 338px bar and pushing everything // else into the overflow menu. assert.ok(layout.rigSelect <= 112, `the rig picker is ${layout.rigSelect}px wide at ${width}px`); } // Hiding the map's filters leaves the bar, and the bar has to still be // there to bring them back — it carries the only button that does. await page.setViewportSize({ width: 1400, height: 900 }); await page.goto(`${fixture.origin}/map`, { waitUntil: "domcontentloaded" }); await page.locator("#aprs-map .leaflet-pane").first().waitFor({ state: "attached" }); await page.waitForTimeout(1200); await page.locator("#map-overlay-toggle-btn").click(); await page.waitForTimeout(400); const collapsed = await page.evaluate(() => { const panel = document.querySelector(".map-overlay-panel"); const style = getComputedStyle(panel); const rect = panel.getBoundingClientRect(); const button = document.getElementById("map-overlay-toggle-btn").getBoundingClientRect(); return { width: Math.round(rect.width), height: Math.round(rect.height), opacity: Number(style.opacity), visibility: style.visibility, display: style.display, buttonWidth: Math.round(button.width), }; }); assert.ok(collapsed.width > 0 && collapsed.height > 0, `the collapsed bar measures ${collapsed.width}x${collapsed.height}`); assert.equal(collapsed.visibility, "visible", "the collapsed bar is not visible"); assert.notEqual(collapsed.display, "none", "the collapsed bar is display:none"); assert.equal(collapsed.opacity, 1, `the collapsed bar is at opacity ${collapsed.opacity}`); assert.ok(collapsed.buttonWidth > 0, "Show Filters has no size to click"); assert.deepEqual(runtimeErrors, []); } finally { await browser.close(); await fixture.close(); }