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
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/133-after-new-app-land-on-the-app-s-page-the → main 1 commit fork
3 files
changed +97
−6
src/web/pages/apps.ts +1 −1
| @@ -531,7 +531,7 @@ export async function newAppSubmit(ctx: Ctx): Promise<Response> { | ||
| 531 | 531 | : plan |
| 532 | 532 | ? ` ${plan.agent} was picked for #${issue.number} — ${plan.summary}` |
| 533 | 533 | : ` #${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}` }); | |
| 535 | 535 | } |
| 536 | 536 | |
| 537 | 537 | // ---- 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)", () | ||
| 1280 | 1280 | }); |
| 1281 | 1281 | expect(res.status).toBe(303); |
| 1282 | 1282 | expect(res.headers.get("location")).toBe( |
| 1283 | "/nadia/keep-a-shared-grocery-list", | |
| 1283 | "/nadia/keep-a-shared-grocery-list/app", | |
| 1284 | 1284 | ); |
| 1285 | 1285 | }); |
| 1286 | 1286 | |
| @@ -1290,7 +1290,7 @@ describe("New app: the form asks for the sentence, and names it (issue #60)", () | ||
| 1290 | 1290 | }); |
| 1291 | 1291 | expect(res.status).toBe(303); |
| 1292 | 1292 | expect(res.headers.get("location")).toBe( |
| 1293 | "/nadia/keep-a-shared-grocery-list-2", | |
| 1293 | "/nadia/keep-a-shared-grocery-list-2/app", | |
| 1294 | 1294 | ); |
| 1295 | 1295 | }); |
| 1296 | 1296 | |
| @@ -1308,6 +1308,97 @@ describe("New app: the form asks for the sentence, and names it (issue #60)", () | ||
| 1308 | 1308 | description: "Keep a shared grocery list", |
| 1309 | 1309 | }); |
| 1310 | 1310 | 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); | |
| 1312 | 1403 | }); |
| 1313 | 1404 | }); |
test/web-apps.test.ts +2 −2
| @@ -154,7 +154,7 @@ describe("apps, runs and the crew on the web", () => { | ||
| 154 | 154 | ); |
| 155 | 155 | }); |
| 156 | 156 | |
| 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 () => { | |
| 158 | 158 | const created = await web(alice, "/apps/new", { |
| 159 | 159 | form: { |
| 160 | 160 | owner: "alice", |
| @@ -164,7 +164,7 @@ describe("apps, runs and the crew on the web", () => { | ||
| 164 | 164 | }, |
| 165 | 165 | }); |
| 166 | 166 | expect(created.status).toBe(303); |
| 167 | expect(created.headers.get("location")).toBe("/alice/hello"); | |
| 167 | expect(created.headers.get("location")).toBe("/alice/hello/app"); | |
| 168 | 168 | expect(flashOf(created)).toContain( |
| 169 | 169 | `hello is starting at http://hello.${DOMAIN}:${servers.port}`, |
| 170 | 170 | ); |
new above
offline