返回 DeepSeek-Reasonix
mobile-nav-contract.test.mjs
根目录 / site / src / scripts / mobile-nav-contract.test.mjs
1 import assert from "node:assert/strict";
2 import { readFile } from "node:fs/promises";
3 import test from "node:test";
4
5 const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
6
7 /* Extract the body of a `@media (max-width: Npx)` block (up to the next
8 at-rule or EOF) so assertions stay scoped to small viewports. */
9 const mediaBlock = (css, px) => {
10 const marker = `@media (max-width: ${px}px)`;
11 const start = css.indexOf(marker);
12 assert.notEqual(start, -1, `missing ${marker}`);
13 const rest = css.slice(start + marker.length);
14 const next = rest.search(/@media|@keyframes/);
15 return next === -1 ? rest : rest.slice(0, next);
16 };
17
18 /* The header must fit a 390px viewport: brand logo + language switch +
19 theme switch + install button. These contraction rules are what keep the
20 nav from overflowing horizontally (body has overflow-x: hidden). */
21 test("≤640px: marketing nav contracts to fit 390px viewports", async () => {
22 const css = await source("../styles/global.css");
23 const block = mediaBlock(css, 640);
24 assert.match(block, /\.nav-sign-in \{ display: none/);
25 assert.match(block, /\.nav \.brand span \{ display: none/);
26 assert.match(block, /\.theme-switch button \{ padding: 6px 9px/);
27 });
28
29 test("≤1100px: marketing navigation collapses before controls overlap", async () => {
30 const css = await source("../styles/global.css");
31 const tabletBlock = mediaBlock(css, 1100);
32 const mobileBlock = mediaBlock(css, 900);
33
34 assert.match(tabletBlock, /\.nav-links \{ display: none/);
35 assert.match(tabletBlock, /\.nav-github \{ margin-left: auto; padding-inline: 8px/);
36 assert.match(tabletBlock, /\.nav-github-label \{ display: none/);
37 assert.match(mobileBlock, /\.nav-github \{ display: none/);
38 });
39
40 test("≤360px: marketing nav keeps every control within 320px", async () => {
41 const css = await source("../styles/global.css");
42 const block = mediaBlock(css, 360);
43 assert.match(block, /\.nav-inner \{ padding: 0 12px; gap: 6px/);
44 assert.match(block, /\.lang-switch button \{ padding: 6px 8px/);
45 assert.match(block, /\.theme-switch button \{ padding: 6px 7px/);
46 assert.match(block, /\.nav \.btn \{ padding: 9px 14px/);
47 });
48
49 test("≤440px: community nav budgets for the async account control", async () => {
50 const css = await source("../styles/community.css");
51 const layout = await source("../layouts/Community.astro");
52 const block = mediaBlock(css, 440);
53 assert.match(layout, /class="brand-name">Reasonix/);
54 assert.match(layout, /id="nav-account"/);
55 assert.match(block, /\.nav \.brand-name \{ display: none/);
56 assert.match(block, /\.nav-right \{ gap: 8px/);
57 assert.match(block, /\.lang-switch button \{ padding: 6px 8px/);
58 assert.match(block, /\.theme-switch button \{ padding: 6px 7px/);
59 assert.match(block, /#nav-account \.btn \{ padding: 7px 10px/);
60 });
61
61 lines Plain Text