Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

68 linesCodeBlameRaw
1import assert from "node:assert/strict";
2import { test } from "node:test";
3import { createMemoryRouter, redirect } from "react-router";
4import { pageOwnsNavigation, pendingNavigationOwns, sidebarLocation } from "./navigation-owner.ts";
5
6test("an old page loses navigation ownership as soon as a newer click starts", async () => {
7 let finish!: (value: Response) => void;
8 const slow = new Promise<Response>((resolve) => { finish = resolve; });
9 const router = createMemoryRouter([
10 { path: "/home" },
11 { path: "/artifacts", loader: () => slow },
12 { path: "/code", loader: () => "latest" },
13 { path: "/old-redirect" },
14 ], { initialEntries: ["/home"] });
15 try {
16 const homeKey = router.state.location.key;
17 assert.equal(pageOwnsNavigation(router.state, homeKey), true);
18 const first = router.navigate("/artifacts");
19 const firstKey = router.state.navigation.location?.key;
20 assert.equal(sidebarLocation(router.state).pathname, "/artifacts", "sidebar follows the pending click immediately");
21 assert.equal(pageOwnsNavigation(router.state, homeKey), false, "old effects must not override a pending click");
22 assert.equal(pendingNavigationOwns(router.state.navigation, firstKey), true);
23 await router.navigate("/code");
24 assert.equal(sidebarLocation(router.state).pathname, "/code");
25 finish(redirect("/old-redirect"));
26 await first;
27 assert.equal(router.state.location.pathname, "/code");
28 assert.equal(sidebarLocation(router.state).pathname, "/code", "late redirects cannot move the sidebar back");
29 assert.equal(pageOwnsNavigation(router.state, homeKey), false);
30 assert.equal(pendingNavigationOwns(router.state.navigation, firstKey), false, "an old recovery timer must not fire");
31 await router.navigate("/home");
32 assert.equal(pageOwnsNavigation(router.state, homeKey), false, "returning to the same URL is a new visit");
33 } finally { router.dispose(); }
34});
35
36test("repeated clicks to the same path have different recovery owners", () => {
37 assert.equal(pendingNavigationOwns({ state: "loading", location: { key: "second-click" } }, "first-click"), false);
38 assert.equal(pendingNavigationOwns({ state: "submitting", location: { key: "first-click" } }, "first-click"), false);
39});
40
41for (const [firstPath, latestPath] of [["/artifacts/one", "/artifacts/two"], ["/artifacts/one", "/code"]]) {
42 test(`latest sidebar destination wins: ${firstPath} then ${latestPath}`, async () => {
43 let finishFirst!: (value: string) => void;
44 let finishLatest!: (value: string) => void;
45 const firstLoad = new Promise<string>((resolve) => { finishFirst = resolve; });
46 const latestLoad = new Promise<string>((resolve) => { finishLatest = resolve; });
47 const router = createMemoryRouter([
48 { path: "/home" },
49 { path: firstPath, loader: () => firstLoad },
50 { path: latestPath, loader: () => latestLoad },
51 ], { initialEntries: ["/home"] });
52 const seen: string[] = [];
53 const unsubscribe = router.subscribe((state) => seen.push(sidebarLocation(state).pathname));
54 try {
55 const first = router.navigate(firstPath);
56 const latest = router.navigate(latestPath);
57 assert.equal(sidebarLocation(router.state).pathname, latestPath);
58 finishFirst("stale");
59 await first;
60 assert.equal(router.state.location.pathname, "/home", "stale page must not commit while the latest page loads");
61 assert.equal(sidebarLocation(router.state).pathname, latestPath);
62 finishLatest("latest");
63 await latest;
64 assert.equal(router.state.location.pathname, latestPath);
65 assert.deepEqual(seen, [firstPath, latestPath, latestPath], "subscribers never see the stale destination again");
66 } finally { unsubscribe(); router.dispose(); }
67 });
68}