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.

78 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) => {
9 finish = resolve;
10 });
11 const router = createMemoryRouter(
12 [{ path: "/home" }, { path: "/artifacts", loader: () => slow }, { path: "/code", loader: () => "latest" }, { path: "/old-redirect" }],
13 { initialEntries: ["/home"] },
14 );
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 {
34 router.dispose();
35 }
36});
37
38test("repeated clicks to the same path have different recovery owners", () => {
39 assert.equal(pendingNavigationOwns({ state: "loading", location: { key: "second-click" } }, "first-click"), false);
40 assert.equal(pendingNavigationOwns({ state: "submitting", location: { key: "first-click" } }, "first-click"), false);
41});
42
43for (const [firstPath, latestPath] of [
44 ["/artifacts/one", "/artifacts/two"],
45 ["/artifacts/one", "/code"],
46]) {
47 test(`latest sidebar destination wins: ${firstPath} then ${latestPath}`, async () => {
48 let finishFirst!: (value: string) => void;
49 let finishLatest!: (value: string) => void;
50 const firstLoad = new Promise<string>((resolve) => {
51 finishFirst = resolve;
52 });
53 const latestLoad = new Promise<string>((resolve) => {
54 finishLatest = resolve;
55 });
56 const router = createMemoryRouter([{ path: "/home" }, { path: firstPath, loader: () => firstLoad }, { path: latestPath, loader: () => latestLoad }], {
57 initialEntries: ["/home"],
58 });
59 const seen: string[] = [];
60 const unsubscribe = router.subscribe((state) => seen.push(sidebarLocation(state).pathname));
61 try {
62 const first = router.navigate(firstPath);
63 const latest = router.navigate(latestPath);
64 assert.equal(sidebarLocation(router.state).pathname, latestPath);
65 finishFirst("stale");
66 await first;
67 assert.equal(router.state.location.pathname, "/home", "stale page must not commit while the latest page loads");
68 assert.equal(sidebarLocation(router.state).pathname, latestPath);
69 finishLatest("latest");
70 await latest;
71 assert.equal(router.state.location.pathname, latestPath);
72 assert.deepEqual(seen, [firstPath, latestPath, latestPath], "subscribers never see the stale destination again");
73 } finally {
74 unsubscribe();
75 router.dispose();
76 }
77 });
78}