Public: anyone can read this; the crew can change it.
The owner's bets page fits a 390 px phone: no sideways scroll and no proposed bet card taller than the screen #9286
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/9275-the-owner-s-bets-page-fits-a-390-px-phon → main 3 commits fork 2 threads · 2 unresolved
2 files
changed +30
−1
src/spawn/preview-looks.ts +4
| @@ -41,6 +41,9 @@ export const PREVIEW_FORKS: readonly { readonly owner: string; readonly of?: str | ||
| 41 | 41 | { owner: "preview-dee", of: "preview-ada" }, |
| 42 | 42 | ]; |
| 43 | 43 | |
| 44 | /** The owner's bets page, whose seeded proposed bets hold the long-name worst case (#9275). */ | |
| 45 | export const PREVIEW_BETS_PAGE = `${PREVIEW_REPO_HOME}/bets`; | |
| 46 | ||
| 44 | 47 | /** The Later gate's proposal on the sample (#9212): what the gate holds, grouped by theme. */ |
| 45 | 48 | export const PREVIEW_HELD_PAGE = `${PREVIEW_REPO_HOME}/held`; |
| 46 | 49 | /** |
| @@ -73,6 +76,7 @@ export const PREVIEW_OWNER_LOOKS: readonly OwnerLook[] = [ | ||
| 73 | 76 | PREVIEW_PULL_PAGE, |
| 74 | 77 | PREVIEW_ITERATIONS_PAGE, |
| 75 | 78 | PREVIEW_HELD_PAGE, |
| 79 | PREVIEW_BETS_PAGE, | |
| 76 | 80 | ].flatMap((page) => (["desktop", "phone"] as const).map((kind) => ({ page, kind }))); |
| 77 | 81 | |
| 78 | 82 | /** |
test/bets-card-fit.test.ts +26 −1
| @@ -11,8 +11,9 @@ import { join } from "node:path"; | ||
| 11 | 11 | import { createApp, type App } from "../src/app.ts"; |
| 12 | 12 | import { serve, type Servers } from "../src/http/server.ts"; |
| 13 | 13 | import { seedPreview } from "../src/spawn/preview-seed.ts"; |
| 14 | import { PREVIEW_ADMIN, PREVIEW_REPO } from "../src/spawn/preview-looks.ts"; | |
| 14 | import { PREVIEW_ADMIN, PREVIEW_BETS_PAGE, PREVIEW_REPO, ownerLooksFor } from "../src/spawn/preview-looks.ts"; | |
| 15 | 15 | import { utilities as base } from "../src/web/style/base.ts"; |
| 16 | import { components } from "../src/web/style/components.ts"; | |
| 16 | 17 | import { content } from "../src/web/style/content.ts"; |
| 17 | 18 | import { SHOWN_JOURNEYS } from "../src/web/pages/bets.ts"; |
| 18 | 19 | |
| @@ -21,6 +22,7 @@ let dir: string; | ||
| 21 | 22 | let app: App; |
| 22 | 23 | let servers: Servers; |
| 23 | 24 | let cards: string[]; |
| 25 | let page: string; | |
| 24 | 26 | |
| 25 | 27 | beforeAll(async () => { |
| 26 | 28 | dir = await mkdtemp(join(tmpdir(), "forge-bets-fit-")); |
| @@ -31,6 +33,7 @@ beforeAll(async () => { | ||
| 31 | 33 | const said = `u1|trevato|admin|${Date.now() + 60_000}`; |
| 32 | 34 | const headers = { "x-forge-identity": said, "x-forge-signature": createHmac("sha256", KEY).update(said).digest("base64url") }; |
| 33 | 35 | const html = await (await fetch(`${servers.url}/${PREVIEW_ADMIN}/${PREVIEW_REPO}/bets`, { headers })).text(); |
| 36 | page = html; | |
| 34 | 37 | cards = html.split("<div data-bet-card").slice(1).map((c) => c.slice(0, c.indexOf("Cut it") + 40)); |
| 35 | 38 | }, 30_000); |
| 36 | 39 | afterAll(async () => { |
| @@ -86,4 +89,26 @@ describe("the owner's bets page, a proposed bet card at rest", () => { | ||
| 86 | 89 | test("long words wrap", () => { |
| 87 | 90 | expect(content).toMatch(/\[data-bet-card\][^{]*\{[^}]*overflow-wrap:\s*anywhere/); |
| 88 | 91 | }); |
| 92 | ||
| 93 | test("the long proposed bet is on the page, and no rule or element it ships fixes a width over 390 px (#9275)", () => { | |
| 94 | expect(page).toContain("Make every long and winding bet name wrap"); | |
| 95 | expect(page).toContain("https://example.com/a/very/long/unbroken/address"); | |
| 96 | for (const m of page.matchAll(/\swidth=["']?(\d+)/gi)) expect(Number(m[1])).toBeLessThanOrEqual(390); | |
| 97 | // Every stylesheet the layout ships: a width or min-width is a fixed size | |
outdated
Duplicates the existing 'long words wrap' test above. Remove it and check the page's stylesheet rules for widths over 390px instead. Allow single quotes too: /\swidth=["']?(\d+)/gi, so width='500' is caught. |
||
| 98 | // (max-width is a cap and shrinks; a "(min-width:" media query is a breakpoint, not a size). px, rem/em at 16 px, ch at 8 px. | |
| 99 | const over: string[] = []; | |
| 100 | for (const css of [base, components, content, ...(page.match(/<style[^>]*>[\s\S]*?<\/style>/g) ?? [])]) { | |
| 101 | for (const m of css.matchAll(/(?<![-\w(])(?:min-)?width\s*:\s*(-?[\d.]+)(px|rem|em|ch)\b/g)) { | |
| 102 | const px = Number(m[1]) * (m[2] === "px" ? 1 : m[2] === "ch" ? 8 : 16); | |
| 103 | if (px > 390) over.push(m[0]); | |
| 104 | } | |
| 105 | } | |
| 106 | expect(over).toEqual([]); | |
| 107 | }); | |
| 108 | }); | |
| 109 | ||
| 110 | test("a forge preview takes the owner's bets page at desk and phone (#9275)", () => { | |
| 111 | const looks = ownerLooksFor("trevato/forge", { ownerName: "trevato", name: "forge" }, "preview"); | |
| 112 | expect(PREVIEW_BETS_PAGE).toBe("/preview-admin/sample/bets"); | |
| 113 | for (const kind of ["desktop", "phone"] as const) expect(looks).toContainEqual({ page: PREVIEW_BETS_PAGE, kind }); | |
| 89 | 114 | }); |
new above
offline
Only HTML attributes and inline styles are scanned. A CSS rule with a fixed width over 390px would pass. Scan the stylesheet the page ships.