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

After New app, land on the app's page — the redirect, not the flash #138

Merged opened this

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

Merged

scout/forge:scout/133-after-new-app-land-on-the-app-s-page-the → main 1 commit fork

  • Merged. scout/133-after-new-app-land-on-the-app-s-page-the was merged into main as ecd81cc.

Review

Sign in to review this change.
3 files changed +97 −6
src/web/pages/apps.ts +1 −1
@@ -531,7 +531,7 @@ export async function newAppSubmit(ctx: Ctx): Promise<Response> {
531531 : plan
532532 ? ` ${plan.agent} was picked for #${issue.number} — ${plan.summary}`
533533 : ` #${issue.number} is assigned to ${issue.assigneeName}.`;
534 return flashTo(ctx, repoBase(repo), { message: `${first}${second}` });
534 return flashTo(ctx, `${repoBase(repo)}/app`, { message: `${first}${second}` });
535535 }
536536
537537 // ---- the repo home's strip ----
test/apps.test.ts +94 −3
@@ -1280,7 +1280,7 @@ describe("New app: the form asks for the sentence, and names it (issue #60)", ()
12801280 });
12811281 expect(res.status).toBe(303);
12821282 expect(res.headers.get("location")).toBe(
1283 "/nadia/keep-a-shared-grocery-list",
1283 "/nadia/keep-a-shared-grocery-list/app",
12841284 );
12851285 });
12861286
@@ -1290,7 +1290,7 @@ describe("New app: the form asks for the sentence, and names it (issue #60)", ()
12901290 });
12911291 expect(res.status).toBe(303);
12921292 expect(res.headers.get("location")).toBe(
1293 "/nadia/keep-a-shared-grocery-list-2",
1293 "/nadia/keep-a-shared-grocery-list-2/app",
12941294 );
12951295 });
12961296
@@ -1308,6 +1308,97 @@ describe("New app: the form asks for the sentence, and names it (issue #60)", ()
13081308 description: "Keep a shared grocery list",
13091309 });
13101310 expect(res.status).toBe(303);
1311 expect(res.headers.get("location")).toBe("/nadia/groceries");
1311 expect(res.headers.get("location")).toBe("/nadia/groceries/app");
1312 });
1313 });
1314
1315 describe("New app lands on the app's page, not the flash (issue #133)", () => {
1316 let dataDir: string;
1317 let app: App;
1318 let servers: Servers;
1319 let runtime: AppsRuntime;
1320 let base: string;
1321 let cookie: string;
1322 const DOMAIN = "apps.test";
1323
1324 beforeAll(async () => {
1325 dataDir = await mkdtemp(join(tmpdir(), "forge-app-redirect-"));
1326 app = createApp({ dataDir, port: 0 });
1327 servers = await serve(app);
1328 base = servers.url;
1329 runtime = new AppsRuntime(app, {
1330 domain: DOMAIN,
1331 scheme: "http",
1332 publicPort: servers.port,
1333 forgeUrl: servers.url,
1334 concurrency: 1,
1335 });
1336 await runtime.start();
1337 const created = await createUser(app.db, {
1338 username: "priya",
1339 email: "[email protected]",
1340 password: "a-good-passphrase",
1341 adminIfFirst: true,
1342 });
1343 if (!created.ok) throw new Error(created.error);
1344 const madeAgent = await createAgent(app, created.value, {
1345 username: "bot",
1346 brief: "Be brief.",
1347 });
1348 if (!madeAgent.ok) throw new Error(String(madeAgent.error));
1349 cookie = (
1350 (
1351 await fetch(`${base}/signin`, {
1352 method: "POST",
1353 body: new URLSearchParams({
1354 username: "priya",
1355 password: "a-good-passphrase",
1356 }),
1357 redirect: "manual",
1358 })
1359 ).headers.get("set-cookie") ?? ""
1360 ).split(";")[0]!;
1361 });
1362
1363 afterAll(async () => {
1364 await runtime.stop();
1365 servers.stop();
1366 app.close();
1367 await rm(dataDir, { recursive: true, force: true });
1368 });
1369
1370 test("POST /apps/new redirects to the app's page, which shows the name, the URL and the first issue", async () => {
1371 const submitted = await fetch(`${base}/apps/new`, {
1372 method: "POST",
1373 headers: { cookie, origin: base },
1374 body: new URLSearchParams({
1375 owner: "priya",
1376 name: "planner",
1377 description: "Plan a trip",
1378 visibility: "private",
1379 agent: "bot",
1380 }),
1381 redirect: "manual",
1382 });
1383 expect(submitted.status).toBe(303);
1384 expect(submitted.headers.get("location")).toBe("/priya/planner/app");
1385 const flash = (submitted.headers.get("set-cookie") ?? "").split(";")[0]!;
1386 expect(flash).toStartWith("forge_flash=");
1387
1388 const landed = await fetch(`${base}${submitted.headers.get("location")}`, {
1389 headers: { cookie: `${cookie}; ${flash}` },
1390 });
1391 expect(landed.status).toBe(200);
1392 const html = await landed.text();
1393 expect(html).toContain("planner");
1394 expect(html).toContain(`http://planner.${DOMAIN}:${servers.port}`);
1395 expect(html).toContain("#1 is assigned to bot.");
1396 });
1397
1398 test("a reader who cannot see the app gets the forge's not-found", async () => {
1399 const anonymous = await fetch(`${base}/priya/planner/app`, {
1400 redirect: "manual",
1401 });
1402 expect(anonymous.status).toBe(404);
13121403 });
13131404 });
test/web-apps.test.ts +2 −2
@@ -154,7 +154,7 @@ describe("apps, runs and the crew on the web", () => {
154154 );
155155 });
156156
157 test("New app creates the app and lands on the repository, URL in hand", async () => {
157 test("New app creates the app and lands on its own page, URL in hand", async () => {
158158 const created = await web(alice, "/apps/new", {
159159 form: {
160160 owner: "alice",
@@ -164,7 +164,7 @@ describe("apps, runs and the crew on the web", () => {
164164 },
165165 });
166166 expect(created.status).toBe(303);
167 expect(created.headers.get("location")).toBe("/alice/hello");
167 expect(created.headers.get("location")).toBe("/alice/hello/app");
168168 expect(flashOf(created)).toContain(
169169 `hello is starting at http://hello.${DOMAIN}:${servers.port}`,
170170 );

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs