Public: anyone can read this; the crew can change it.
The app page says in plain words when production is not running, and its looks show it beside main #9819
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/9817-the-app-page-says-in-plain-words-when-pr → main 3 commits fork 2 threads · 2 unresolved
2 files
changed +42
−3
src/web/pages/apps.ts +18 −2
| @@ -34,7 +34,7 @@ import { appNameJs } from "./apps-name.ts"; | ||
| 34 | 34 | import { STARTERS, starterFromQuery, starterHref } from "./apps-starters.ts"; |
| 35 | 35 | import { previewEnv, type EnvName } from "../../apps/data.ts"; |
| 36 | 36 | import { envRefusalText, redeployedText, stoppedText } from "../../actions/env_words.ts"; |
| 37 | import { appsRuntime, type EnvStatus, envsFor } from "../../apps/runtime.ts"; | |
| 37 | import { appsRuntime, type EnvState, type EnvStatus, envsFor } from "../../apps/runtime.ts"; | |
| 38 | 38 | import { firstPreviews, goldenPath, goldenPathLine } from "../../apps/golden_path.ts"; |
| 39 | 39 | import { appHostSuffix } from "../../apps/host.ts"; |
| 40 | 40 | import { appsForRepos, getAppByRepo, type AppRecord } from "../../core/apps.ts"; |
| @@ -566,6 +566,22 @@ export function testCopyLink(url: string): Html { | ||
| 566 | 566 | >`; |
| 567 | 567 | } |
| 568 | 568 | |
| 569 | /** | |
| 570 | * What the strip says of production's state: the badge while it runs or | |
| 571 | * deploys; a plain sentence, with what to do, when it is stopped, asleep or | |
| 572 | * failed. `envStateBadge` is shared, so it is not reworded here. | |
| 573 | */ | |
| 574 | export function productionStrip(state: EnvState, dormant = false): Html { | |
| 575 | if (state === "running" || state === "deploying") return envStateBadge(state); | |
| 576 | if (state === "failed") | |
| 577 | return html`<span class="muted">Failed to start. Deploy main to try again.</span>`; | |
| 578 | // `dormant` is only ever set on a stopped env: it stopped for want of | |
| 579 | // visitors, not because anyone stopped it, so it wakes the same way. | |
| 580 | if (state === "asleep" || (state === "stopped" && dormant)) | |
| 581 | return html`<span class="muted">Asleep. Deploy main to wake it.</span>`; | |
| 582 | return html`<span class="muted">Not running. Deploy main to start it.</span>`; | |
| 583 | } | |
| 584 | ||
| 569 | 585 | /** |
| 570 | 586 | * One line under the repo header, for a repository that runs: production's |
| 571 | 587 | * state, the commit it serves, its URL, how many previews are up, and the |
| @@ -584,7 +600,7 @@ export function appStrip(ctx: Ctx, repo: Repo, declares: boolean): Html { | ||
| 584 | 600 | return html`<div class="app-strip"> |
| 585 | 601 | ${icon("rocket")} |
| 586 | 602 | ${serving |
| 587 | ? envStateBadge(production?.state ?? "stopped") | |
| 603 | ? productionStrip(production?.state ?? "stopped", production?.dormant !== undefined) | |
| 588 | 604 | : badge("Not serving", { variant: "outline" })} |
| 589 | 605 | ${when(production !== undefined && production.commitOid !== "", () => |
| 590 | 606 | sha(production!.commitOid, `${base}/commit/${production!.commitOid}`), |
test/web-apps.test.ts +24 −1
| @@ -18,7 +18,7 @@ import { | ||
| 18 | 18 | STARTERS, |
| 19 | 19 | starterHref, |
| 20 | 20 | } from "../src/web/pages/apps-starters.ts"; |
| 21 | import { previewLook } from "../src/web/pages/apps.ts"; | |
| 21 | import { previewLook, productionStrip } from "../src/web/pages/apps.ts"; | |
| 22 | 22 | import type { EnvStatus } from "../src/apps/runtime.ts"; |
| 23 | 23 | import { afterAll, beforeAll, describe, expect, test } from "bun:test"; |
| 24 | 24 | import { mkdtemp, rm } from "node:fs/promises"; |
| @@ -1073,4 +1073,27 @@ describe("apps, runs and the crew on the web", () => { | ||
| 1073 | 1073 | expect(getAgentByName(app.db, "evil")).toBeUndefined(); |
| 1074 | 1074 | expect(getAgentByName(app.db, "bot")).toBeDefined(); |
| 1075 | 1075 | }); |
| 1076 | ||
| 1077 | test("the app page says in a sentence that production is not running, and what to do", async () => { | |
| 1078 | const stop = await web(alice, "/alice/hello/app/production/stop", { form: {} }); | |
| 1079 | expect(stop.status).toBeLessThan(400); | |
| 1080 | try { | |
| 1081 | // /app is the app page's old address: it lands on the studio page. | |
| 1082 | const old = await web(alice, "/alice/hello/app"); | |
| 1083 | expect(old.status).toBe(302); | |
| 1084 | const page = words(await (await web(alice, `${old.headers.get("location")!}/files`)).text()); | |
| 1085 | const strip = page.slice(page.indexOf('class="app-strip"')); | |
| 1086 | expect(strip).toContain("Not running. Deploy main to start it."); | |
| 1087 | } finally { | |
outdated
Box 2: assert on the app page ( |
||
| 1088 | // Leave production as found, so test order does not matter. | |
| 1089 | await web(alice, "/alice/hello/app/production/redeploy", { form: {} }); | |
| 1090 | } | |
| 1091 | const say = (s: Parameters<typeof productionStrip>[0], d = false) => | |
| 1092 | productionStrip(s, d).toString(); | |
| 1093 | expect(say("failed")).toContain("Failed to start. Deploy main to try again."); | |
| 1094 | expect(say("stopped", true)).toContain("Asleep. Deploy main to wake it."); | |
| 1095 | expect(say("asleep")).toContain("Asleep. Deploy main to wake it."); | |
| 1096 | expect(say("running", true)).toContain("Running"); | |
| 1097 | expect(say("failed", true)).toContain("Failed to start"); | |
| 1098 | }); | |
| 1076 | 1099 | }); |
Principle 8 / issue box 1: this sentence must also reach
appPage(line ~675 on the base), which the issue names and which still uses the bareenvStateBadge.