tests/gui-layout.test.js
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: GPL-3.0-or-later
/**
* Layout gates. The wide-desktop shell has TWO gates that must agree:
*
* - `src-gui/css/wide.css` — the `@media (min-width: …)` block
* - `src-gui/js/ui-wide.js` — `WIDE_QUERY`, which toggles `html.wide`
*
* They were written as the same number by hand, and nothing stopped them from
* drifting apart: change one and the other silently keeps the old behaviour
* (the CSS would uncap at one width while the JS opens the groups at another).
* The built `src-gui/index.html` is a third copy — it is a committed artifact,
* so it can also lag behind the sources.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
const read = (p) => readFileSync(new URL(`../${p}`, import.meta.url), "utf8");
/** The CSS gate: the min-width of the single @media block in wide.css. */
function cssBreakpoint() {
const css = read("src-gui/css/wide.css");
const m = /@media\s*\(\s*min-width:\s*(\d+)px\s*\)/.exec(css);
return m ? Number(m[1]) : null;
}
/** The JS gate: WIDE_QUERY's min-width. */
function jsBreakpoint() {
const js = read("src-gui/js/ui-wide.js");
const m = /WIDE_QUERY\s*=\s*"\(min-width:\s*(\d+)px\)"/.exec(js);
return m ? Number(m[1]) : null;
}
test("wide: both gates declare a breakpoint and they agree", () => {
const css = cssBreakpoint();
const js = jsBreakpoint();
assert.ok(css, "wide.css must declare a @media (min-width: …) block");
assert.ok(js, "ui-wide.js must declare WIDE_QUERY");
assert.equal(
css,
js,
"wide.css and ui-wide.js must use the SAME breakpoint — the CSS uncaps the shell and the JS opens the groups"
);
});
test("wide: the breakpoint stays in the desktop range", () => {
const bp = cssBreakpoint();
// Below 1100 the sidebar rail (responsive.css) has not appeared yet, so a
// master-detail pane there would be a mobile screen with two columns. Above
// 1440 is the old value that left ordinary laptops without the workbench.
assert.ok(bp >= 1100 && bp <= 1440, `unexpected wide breakpoint: ${bp}px`);
});
test("wide: the committed index.html carries both gates at that breakpoint", () => {
const bp = cssBreakpoint();
const html = read("src-gui/index.html");
const hits = html.match(new RegExp(`min-width:\\s*${bp}px`, "g")) ?? [];
assert.equal(
hits.length,
2,
`index.html must contain the CSS gate and the JS gate (found ${hits.length}) — run: npm run build:gui`
);
});
/**
* The phone layout stacked the toolbar controls into a column, so every button
* stretched to full width: six rows of chrome before the first finding. It was
* fixed to wrap instead, and this is the guard against it coming back.
*/
test("mobile: the toolbar wraps instead of stacking full-width controls", () => {
const css = read("src-gui/css/responsive.css");
const block = /@media\s*\(max-width:\s*700px\)\s*\{([\s\S]*?)\n\}/.exec(css)?.[1] ?? "";
assert.ok(block, "responsive.css must keep a ≤700px block");
const toolbar = /\.toolbar\s*\{([^}]*)\}/.exec(block)?.[1] ?? "";
assert.ok(toolbar, "the ≤700px block must style .toolbar");
assert.match(toolbar, /flex-wrap:\s*wrap/, ".toolbar must wrap");
assert.doesNotMatch(
toolbar,
/flex-direction:\s*column/,
"a column stretches every control to full width — six rows of chrome before the first finding"
);
});