tests/drawerQuery.test.ts
Rama main · réplica de lectura del repositorio original.
import assert from "node:assert/strict";
import { readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, it } from "node:test";
// ONE PHONE QUESTION, ASKED ONCE (3.27.0).
//
// This file held the drawer breakpoint to one string across client/state.ts
// and every stylesheet — it drifted twice, once in spirit (the stylesheet said
// "phone or no fine pointer", the store "under 1000px") and once for real
// (tokens.css kept `not (any-pointer: fine)` after app.css moved to the
// primary pointer). The drawer is gone now: the Classic phone layout was
// deleted, the desktop shell is never mounted where PHONE_SHELL_QUERY
// matches, and DRAWER_QUERY and PHONE_QUERY went with the overlay they
// decided. What stays true, and is held here: the phone question is ONE
// constant (client/shellQuery.ts), nothing re-spells it in code, no
// stylesheet carries the drawer's 999px band or asks `any-pointer`, and the
// drawer's chrome left no rule behind.
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const shellQuery = readFileSync(path.join(root, "client/shellQuery.ts"), "utf8");
const state = readFileSync(path.join(root, "client/state.ts"), "utf8");
const stylesDir = path.join(root, "client/styles");
const sheets = [
...readdirSync(stylesDir)
.filter((f) => f.endsWith(".css"))
.map((f) => ({ file: `client/styles/${f}`, css: readFileSync(path.join(stylesDir, f), "utf8") })),
{ file: "client/phone/phone.css", css: readFileSync(path.join(root, "client/phone/phone.css"), "utf8") },
];
/** Every `@media` condition in a sheet, with its line. */
function mediaOf(css: string): { line: number; cond: string }[] {
const out: { line: number; cond: string }[] = [];
const re = /@media\s+([^{]+)\{/g;
for (let m = re.exec(css); m; m = re.exec(css)) {
out.push({ line: css.slice(0, m.index).split("\n").length, cond: m[1].trim().replace(/\s+/g, " ") });
}
return out;
}
function sources(dir: string): string[] {
const out: string[] = [];
for (const name of readdirSync(dir)) {
const p = path.join(dir, name);
if (statSync(p).isDirectory()) out.push(...sources(p));
else if (/\.tsx?$/.test(name)) out.push(p);
}
return out;
}
describe("the phone question", () => {
it("is one constant, decided by the primary pointer", () => {
const q = /export const PHONE_SHELL_QUERY = "([^"]+)";/.exec(shellQuery)?.[1];
assert.ok(q, "PHONE_SHELL_QUERY not found");
// THE PRIMARY POINTER, NOT `any-pointer` (3.23.0): `any-pointer: fine` is
// true on every phone that has met a stylus or a bluetooth mouse.
assert.ok(q.includes("(pointer: coarse) and (hover: none)"));
assert.ok(!q.includes("any-pointer"));
assert.ok(!/export const (DRAWER|PHONE)_QUERY/.test(state), "the drawer's two queries went with the drawer");
});
it("no stylesheet carries the drawer's 999px band, and none asks any-pointer", () => {
const drift: string[] = [];
for (const { file, css } of sheets) {
for (const { line, cond } of mediaOf(css)) {
if (/999px/.test(cond) || cond.includes("any-pointer")) drift.push(`${file}:${line} @media ${cond}`);
}
}
assert.deepEqual(drift, [], `a stylesheet still asks the drawer's question:\n ${drift.join("\n ")}`);
});
it("the drawer's chrome left no rule and no element behind", () => {
const chrome = /\.s-drawer-btn|\.s-drawer-backdrop|\.s-app--drawer|__phoneclose|\.s-panel__scrim|--z-drawer/;
const left = sheets.filter(({ css }) => chrome.test(css)).map(({ file }) => file);
assert.deepEqual(left, [], `drawer chrome still styled in:\n ${left.join("\n ")}`);
const code = sources(path.join(root, "client"))
.filter((f) => /s-drawer-btn|s-drawer-backdrop|s-app--drawer|__phoneclose|sidebarOpen|sidebarIsDrawer/.test(readFileSync(f, "utf8")))
.map((f) => path.relative(root, f));
assert.deepEqual(code, [], `drawer chrome still drawn or read in:\n ${code.join("\n ")}`);
});
// The TypeScript copies: `(max-width: 700px)` was once spelled out in five
// files. A NAMED, EXPORTED query is the cure; a string literal is a copy.
it("is imported, never re-spelled, by the client's code", () => {
const copies: string[] = [];
for (const file of sources(path.join(root, "client"))) {
const lines = readFileSync(file, "utf8").split("\n");
lines.forEach((text, i) => {
const code = text.replace(/\/\/.*$/, "");
if (/^\s*\*/.test(code)) return; // a doc comment's body
if (/^\s*export const [A-Z_]+_QUERY = /.test(code)) return;
if (/["'`]\(max-width: (640|700|999)px\)/.test(code) || /any-pointer/.test(code)) {
copies.push(`${path.relative(root, file)}:${i + 1} ${text.trim()}`);
}
});
}
assert.deepEqual(copies, [], `use PHONE_SHELL_QUERY from client/shellQuery.ts:\n ${copies.join("\n ")}`);
});
});