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

/forges/new says the crew starts on the included credit when it applies #11355

Merged opened this

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

Merged

mason/forge:mason/11344-forges-new-says-the-crew-starts-on-the-i → main 3 commits fork 2 threads · 2 unresolved

  • Merged. mason/11344-forges-new-says-the-crew-starts-on-the-i was merged into main as 28935c4.

Review

Sign in to review this change.
2 files changed +31 −1
src/web/pages/new-forge.ts +12 −1
@@ -16,6 +16,7 @@ import type { User } from "../../core/users.ts";
1616 import type { Ctx } from "../../http/context.ts";
1717 import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts";
1818 import { giftOf, REDEEM_WORDS } from "../../actions/codes.ts";
19 import { limitValue } from "../../settings/limits.ts";
1920 import { quotaFull } from "../../spawn/quota.ts";
2021 import { childHost, childUrlOf, noRoom, spawnDoor, type Door } from "../../spawn/door.ts";
2122 import {
@@ -170,6 +171,15 @@ function freshError(errors: Errors, repoCount: number): string | undefined {
170171 return errors["fresh"] ?? (repoCount === 0 ? errors["apps"] : undefined);
171172 }
172173
174 /** The line under the heading: the crew starts on the included credit when there is one, else on the person's AI account. */
175 function hostingTerms(includedUsd: number): string {
176 const crew =
177 includedUsd > 0
178 ? `its crew starts on the included $${includedUsd} of credit, and you can add your AI account key later`
179 : "its crew starts once you add your AI account";
180 return `Free within limits: it sleeps when idle, and ${crew}.`;
181 }
182
173183 function formView(
174184 ctx: Ctx,
175185 user: User | undefined,
@@ -191,6 +201,7 @@ function formView(
191201 </section>
192202 </div>`;
193203 const anon = user === undefined;
204 const includedUsd = limitValue(ctx.app.db, "spawn.included_credit_usd");
194205 const freshErr = freshError(errors, repos.length);
195206 const lead = repos.length === 0 ? "Pick a name, and say what it is for if you like" : "Pick a name and the apps to start from";
196207 return html`<div class="narrow-wide" data-forges>
@@ -199,7 +210,7 @@ function formView(
199210 // /forges answers only an admin; to anyone else the link would lead to a 404.
200211 actions: ctx.auth.user?.isAdmin ? sentLink() : undefined,
201212 })}
202 <p class="muted small" id="hosting-terms">Free within limits: it sleeps when idle, and its crew starts once you add your AI account.</p>
213 <p class="muted small" id="hosting-terms">${hostingTerms(includedUsd)}</p>
outdated

Principle 1 (nothing explains itself twice): the prefix is repeated here, because hostingTerms already begins with "Free within limits: it sleeps when idle, and". Use ${hostingTerms(includedUsd)} alone.

203214 ${quota ? html`<p role="status" id="quota-full">This forge has no room for another forge: ${quota}.</p>` : null}
204215 ${errors["form"]
205216 ? html`<p class="error mb" role="alert">${errors["form"]}</p>`
test/new-forge-language.test.ts +19
@@ -10,6 +10,7 @@ import { serve } from "../src/http/server.ts";
1010 import { usePlacement, useProbe } from "../src/spawn/door.ts";
1111 import { bootHostedChild } from "../src/spawn/host.ts";
1212 import { ok } from "../src/kernel/result.ts";
13 import { setLimit } from "../src/settings/limits.ts";
1314
1415 let dir = "";
1516 let app: ReturnType<typeof createApp>;
@@ -45,6 +46,24 @@ afterAll(async () => {
4546 const main = (page: string): string => page.slice(page.indexOf("<main"), page.indexOf("</main>"));
4647 const boxed = ['class="card', "card-body"];
4748
49 test("the hosting line says the crew starts on the included credit only when there is some", async () => {
50 const say = async () => {
51 const page = main(await (await fetch(servers.url + "/forges/new", { headers: auth })).text());
52 const line = /<p class="muted small" id="hosting-terms">([^]*?)<\/p>/.exec(page)?.[1] ?? "";
53 expect(line.split("Free within limits").length - 1).toBe(1);
54 return line;
55 };
outdated

Substring assertions let the duplicated sentence through. Assert the exact text of #hosting-terms, and that "Free within limits" appears once.

56 const id = app.db.query<{ id: string }, []>("SELECT id FROM user LIMIT 1").get()!.id;
57 setLimit(app.db, "spawn.included_credit_usd", 3, id);
58 expect(await say()).toBe(
59 "Free within limits: it sleeps when idle, and its crew starts on the included $3 of credit, and you can add your AI account key later.",
60 );
61 setLimit(app.db, "spawn.included_credit_usd", 0, id);
62 expect(await say()).toBe(
63 "Free within limits: it sleeps when idle, and its crew starts once you add your AI account.",
64 );
65 });
66
4867 test("the form is a headed section, not a card", async () => {
4968 const page = main(await (await fetch(servers.url + "/forges/new", { headers: auth })).text());
5069 expect(page).toContain('<section class="settings-block">');

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs