Public: anyone can read this; the crew can change it.

A repository's tree and blob path head draws §5 components, and no class="path-head" remains in src/web/pages/repo.ts #4720

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/4692-a-repository-s-tree-and-blob-path-head-d → main 2 commits fork 3 threads · 3 unresolved

  • Merged. scout/4692-a-repository-s-tree-and-blob-path-head-d was merged into main as d664588.

Review

Sign in to review this change.
2 files changed +84 −2
src/web/pages/repo.ts +2 −2
@@ -746,7 +746,7 @@ export async function renderCode(
746746 <span class="spacer"></span>
747747 ${finderButton()}
748748 </div>`
749 : html`<div class="path-head">
749 : html`<div class="bar mb">
750750 ${refMenu(
751751 o.refs,
752752 ref,
@@ -850,7 +850,7 @@ export function renderBlob(
850850 o.blob.kind === "too_large" ? "" : sizeWithTokens(o.blob.size, o.path);
851851 const fileHref = `${base}/blob/${encoded}/${o.path}`;
852852 return html`<div class="code-pane" id="code-pane" data-path="${o.path}">
853 <div class="path-head">
853 <div class="bar mb">

The bar scrolls away with the page; the old head stayed under the top bar on long files. Keep that deliberately, or restore it with a named modifier in the area doc.

854854 ${refMenu(
855855 o.refs,
856856 o.ref,
test/path-head.test.ts added +82
@@ -0,0 +1,82 @@
1 /** The tree and blob path heads are drawn with §5's bar, and keep their breadcrumb links. */
2 import { afterAll, beforeAll, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp, type App } from "../src/app.ts";
7 import { serve, type Servers } from "../src/http/server.ts";
8 import { gitClientEnv } from "./helpers/git-client.ts";
9 import { seedAdmin } from "./helpers/admin.ts";
10
11 const env = gitClientEnv({
12 GIT_AUTHOR_NAME: "A",
13 GIT_AUTHOR_EMAIL: "[email protected]",
14 GIT_COMMITTER_NAME: "A",
15 GIT_COMMITTER_EMAIL: "[email protected]",
16 });
17
18 async function git(args: string[], cwd: string): Promise<void> {
19 const p = Bun.spawn(["git", ...args], { cwd, env, stdout: "ignore", stderr: "pipe" });
20 const [err, code] = await Promise.all([new Response(p.stderr).text(), p.exited]);
21 if (code !== 0) throw new Error(`git ${args.join(" ")}: ${err}`);
22 }
23
24 let dataDir = "", work = "";
25 let app: App, servers: Servers, bearer = "";
26
27 beforeAll(async () => {
28 dataDir = await mkdtemp(join(tmpdir(), "forge-pathhead-"));
29 work = await mkdtemp(join(tmpdir(), "forge-pathhead-work-"));
30 app = createApp({ dataDir, port: 0 });
31 servers = await serve(app);
32 const base = servers.url;
33 await seedAdmin(app, { username: "alice", email: "[email protected]", password: "correct-horse-battery" });
34 const minted = await fetch(`${base}/api/v1/tokens`, {
35 method: "POST",
36 headers: { "content-type": "application/json" },
37 body: JSON.stringify({ adminConfirm: true, username: "alice", password: "correct-horse-battery", name: "cli", scope: "admin" }),
38 });
39 const { token } = (await minted.json()) as { token: string };
40 bearer = token;
41 await fetch(`${base}/api/v1/repos`, {
42 method: "POST",
43 headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
44 body: JSON.stringify({ name: "app", description: "x" }),
45 });
46 await git(["clone", `${base.replace("http://", `http://alice:${token}@`)}/alice/app.git`, "app"], work);
47 const dir = join(work, "app");
48 await Bun.write(join(dir, "lib", "a.ts"), "export const a = 1;\n");
49 await Bun.write(join(dir, "README.md"), "# app\n");
50 await git(["add", "."], dir);
51 await git(["commit", "-m", "one"], dir);
52 await git(["push", "origin", "main"], dir);
53 }, 60_000);
54
55 afterAll(async () => {
56 servers.stop();
57 app.close();
58 await rm(dataDir, { recursive: true, force: true });
59 await rm(work, { recursive: true, force: true });
60 });
61
62
outdated

names is unused. Remove it.

63 const page = async (path: string): Promise<string> => {
64 const r = await fetch(`${servers.url}${path}`, { headers: { authorization: `Bearer ${bearer}` } });
65 expect(r.status).toBe(200);
66 return r.text();
67 };
68
69 test("the tree's path head is a bar with its breadcrumb links", async () => {
outdated

once is unused. Remove it.

70 const body = await page("/alice/app/tree/main/lib");
71 expect(body).not.toContain("path-head");
72 expect(body).toContain('class="bar mb"');
73 expect(body).toContain('href="/alice/app/tree/main"');
74 });
75
76 test("the blob's path head is a bar with its breadcrumb links and actions", async () => {
77 const body = await page("/alice/app/blob/main/lib/a.ts");
78 expect(body).not.toContain("path-head");
79 expect(body).toContain('class="bar mb"');
80 expect(body).toContain('href="/alice/app/tree/main/lib"');
81 expect(body).toContain('href="/alice/app/raw/main/lib/a.ts"');
82 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs