Public: anyone can read this; the crew can change it.
The repository page's "Moved from" line is escaped and wraps at 390px, proven by a test and a phone look #10177
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/10156-the-repository-page-s-moved-from-line-is → main 2 commits fork
1 file
changed +84
−0
test/moved-from-fit.test.ts added +84
| @@ -0,0 +1,84 @@ | ||
| 1 | /** | |
| 2 | * The repository page's "Moved from" line (#10156): an old path holding markup | |
| 3 | * is escaped, and a long old path wraps instead of widening a 390 px phone. | |
| 4 | */ | |
| 5 | import { afterAll, beforeAll, describe, expect, test } from "bun:test"; | |
| 6 | import { mkdtemp, rm } from "node:fs/promises"; | |
| 7 | import { tmpdir } from "node:os"; | |
| 8 | import { join } from "node:path"; | |
| 9 | import { createApp, type App } from "../src/app.ts"; | |
| 10 | import { createRepo, type Repo } from "../src/core/repos.ts"; | |
| 11 | import { transferRepo } from "../src/core/transfer.ts"; | |
| 12 | import { createUser } from "../src/core/users.ts"; | |
| 13 | import { serve, type Servers } from "../src/http/server.ts"; | |
| 14 | import { findChromium } from "../src/looks/capture.ts"; | |
| 15 | import { chromiumRuns, evaluateLoaded } from "./helpers/chrome.ts"; | |
| 16 | ||
| 17 | const OLD = `<b>&"/${"n".repeat(200)}`; | |
| 18 | let dir: string; | |
| 19 | let app: App; | |
| 20 | let servers: Servers; | |
| 21 | let page: string; | |
| 22 | ||
| 23 | beforeAll(async () => { | |
| 24 | dir = await mkdtemp(join(tmpdir(), "forge-moved-fit-")); | |
| 25 | app = createApp({ dataDir: dir, port: 0 }); | |
| 26 | servers = await serve(app); | |
| 27 | const mk = async (username: string) => { | |
| 28 | const u = await createUser(app.db, { origin: "seed", trusted: true, username, email: `${username}@forge.local`, password: "a-long-moved-passphrase", adminIfFirst: username === "alice" }); | |
| 29 | if (!u.ok) throw new Error(u.error); | |
| 30 | return u.value; | |
| 31 | }; | |
| 32 | const alice = await mk("alice"); | |
| 33 | const lab = await mk("lab"); | |
| 34 | const made = await createRepo(app.db, app.reposRoot, { ownerId: alice.id, name: "demo", visibility: "public" }); | |
| 35 | if (!made.ok) throw new Error(made.error); | |
| 36 | const moved = await transferRepo(app.db, app.reposRoot, made.value as Repo, lab, alice.id); | |
| 37 | if (!moved.ok) throw new Error(moved.error); | |
| 38 | app.db.query("UPDATE repo_transfer SET from_path = ?1 WHERE repo_id = ?2").run(OLD, made.value.id); | |
| 39 | page = await (await fetch(`${servers.url}/lab/demo`)).text(); | |
| 40 | }, 30_000); | |
| 41 | ||
| 42 | afterAll(async () => { | |
| 43 | servers.stop(); | |
| 44 | app.close(); | |
| 45 | await rm(dir, { recursive: true, force: true }); | |
| 46 | }); | |
| 47 | ||
| 48 | describe("the moved-from line", () => { | |
| 49 | test("an old path holding markup is escaped in the line", () => { | |
| 50 | const line = page.match(/<p class="muted small moved-from">[\s\S]*?<\/p>/)?.[0] ?? ""; | |
| 51 | expect(line).toContain("Moved from <b>&"/nnnn"); | |
| 52 | expect(line).not.toContain("<b>"); | |
| 53 | expect(page).not.toContain("<b>&"); | |
| 54 | }); | |
| 55 | ||
| 56 | test("the line is server-rendered, plain, and carries no inline style", () => { | |
| 57 | expect(page).toMatch(/<p class="muted small moved-from">Moved from /); | |
| 58 | expect(page).not.toMatch(/<p class="muted small moved-from"[^>]*style=/); | |
| 59 | }); | |
| 60 | }); | |
| 61 | ||
| 62 | /** Measured in a real Chromium at 390x844; skipped where none starts (the forge's check runs it). */ | |
| 63 | const found = findChromium(); | |
| 64 | const chromium = found !== undefined && chromiumRuns(found) ? found : undefined; | |
| 65 | if (chromium === undefined) console.log("SKIPPING: no chromium that starts here, so the moved-from phone look is not measured"); | |
| 66 | ||
| 67 | describe.skipIf(chromium === undefined)("the moved-from line, measured at 390x844", () => { | |
| 68 | const measure = `(() => { | |
| 69 | const el = document.querySelector(".moved-from"); | |
| 70 | const r = el ? el.getBoundingClientRect() : { right: -1, height: 0 }; | |
| 71 | return JSON.stringify({ | |
| 72 | scrollWidth: document.documentElement.scrollWidth, innerWidth: window.innerWidth, | |
| 73 | right: Math.round(r.right), height: Math.round(r.height), lineHeight: parseFloat(getComputedStyle(el).lineHeight), | |
| 74 | }); | |
| 75 | })()`; | |
| 76 | test("a long old path wraps: the page does not scroll sideways", async () => { | |
| 77 | const m = JSON.parse(await evaluateLoaded(chromium!, `${servers.url}/lab/demo`, { width: 390, height: 844 }, [], measure)) as { scrollWidth: number; innerWidth: number; right: number; height: number; lineHeight: number }; | |
| 78 | console.log(`moved-from 390x844: ${JSON.stringify(m)}`); | |
| 79 | expect(m.right).toBeGreaterThan(0); | |
| 80 | expect(m.right).toBeLessThanOrEqual(m.innerWidth); | |
| 81 | expect(m.height).toBeGreaterThan(m.lineHeight * 1.5); | |
| 82 | expect(m.scrollWidth).toBeLessThanOrEqual(m.innerWidth); | |
| 83 | }, 30_000); | |
| 84 | }); |