Public: anyone can read this; the crew can change it.
Home's Terminal block is a plain heading and the ssh command, not a boxed card #10012
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/10005-home-s-terminal-block-is-a-plain-heading → main 2 commits fork
2 files
changed +58
−6
src/web/pages/site.ts +5 −6
| @@ -49,7 +49,6 @@ import { icon } from "../icons.ts"; | ||
| 49 | 49 | import { flashTo, page, redirect } from "../layout.ts"; |
| 50 | 50 | import { |
| 51 | 51 | btn, |
| 52 | card, | |
| 53 | 52 | field as formField, |
| 54 | 53 | iconBtn, |
| 55 | 54 | listCard, |
| @@ -197,14 +196,14 @@ export function orgsCard(ctx: Ctx, user: User): Html { | ||
| 197 | 196 | export function terminalCard(ctx: Ctx): Html { |
| 198 | 197 | const ssh = sshCommand(ctx.app); |
| 199 | 198 | if (ssh === undefined) return html``; |
| 200 | return card( | |
| 201 | terminalCommand( | |
| 199 | return html`<section class="settings-block" id="terminal"> | |
| 200 | <h2>Terminal</h2> | |
| 201 | ${terminalCommand( | |
| 202 | 202 | ssh, |
| 203 | 203 | ctx.app.config.sshHostKey, |
| 204 | 204 | html`<a class="small" href="/settings/keys">SSH keys</a>`, |
| 205 | ), | |
| 206 | { head: html`<h2>Terminal</h2>` }, | |
| 207 | ); | |
| 205 | )} | |
| 206 | </section>`; | |
| 208 | 207 | } |
| 209 | 208 | |
| 210 | 209 | // GET / — the one home: a stranger gets the door, and a signed-in person |
test/home-terminal-block.test.ts added +53
| @@ -0,0 +1,53 @@ | ||
| 1 | /** The feed home's Terminal is a plain block: heading and command, no card (#10005). */ | |
| 2 | import { afterAll, 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 { sshCommand } from "../src/ssh/command.ts"; | |
| 9 | import { adminCookie } from "./helpers/admin.ts"; | |
| 10 | ||
| 11 | let dirs: string[] = []; | |
| 12 | async function boot(sshPort?: number) { | |
| 13 | const dataDir = await mkdtemp(join(tmpdir(), "forge-home-term-")); | |
| 14 | dirs.push(dataDir); | |
| 15 | const app: App = createApp({ dataDir, port: 0, host: "forge.example" }); | |
| 16 | if (sshPort !== undefined) app.config.sshPort = sshPort; | |
| 17 | const servers: Servers = await serve(app); | |
| 18 | const cookie = await adminCookie(app, { username: "ada", email: "[email protected]", password: "analytical-engine" }); | |
| 19 | const page = async () => { | |
| 20 | const t = await (await fetch(`${servers.url}/feed`, { headers: { cookie } })).text(); | |
| 21 | return t.slice(t.indexOf("<main"), t.indexOf("</main>")); | |
| 22 | }; | |
| 23 | return { app, servers, page }; | |
| 24 | } | |
| 25 | afterAll(async () => { | |
| 26 | for (const d of dirs) await rm(d, { recursive: true, force: true }); | |
| 27 | }); | |
| 28 | ||
| 29 | test("terminal on: heading, exact command and keys link, no card", async () => { | |
| 30 | const { app, servers, page } = await boot(2222); | |
| 31 | try { | |
| 32 | const m = await page(); | |
| 33 | const at = m.indexOf("<h2>Terminal</h2>"); | |
| 34 | expect(at).toBeGreaterThan(0); | |
| 35 | const block = m.slice(m.lastIndexOf("<section", at), m.indexOf("</section>", at)); | |
| 36 | expect(block).toContain(sshCommand(app)!); | |
| 37 | expect(block).toContain('href="/settings/keys"'); | |
| 38 | expect(block).not.toContain('class="card'); | |
| 39 | } finally { | |
| 40 | servers.stop(); | |
| 41 | app.close(); | |
| 42 | } | |
| 43 | }); | |
| 44 | ||
| 45 | test("terminal off: no Terminal heading", async () => { | |
| 46 | const { app, servers, page } = await boot(); | |
| 47 | try { | |
| 48 | expect(await page()).not.toContain("<h2>Terminal</h2>"); | |
| 49 | } finally { | |
| 50 | servers.stop(); | |
| 51 | app.close(); | |
| 52 | } | |
| 53 | }); |