| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { |
| 4 | isBaseThreadRoute, |
| 5 | resolveFileSelectionNavigationAction, |
| 6 | resolveThreadSelectionNavigationAction, |
| 7 | resolveThreadSelectionOverlayState, |
| 8 | } from "./adaptive-navigation"; |
| 9 | |
| 10 | describe("isBaseThreadRoute", () => { |
| 11 | it("recognizes only the thread detail route", () => { |
| 12 | expect(isBaseThreadRoute("/threads/environment/thread")).toBe(true); |
| 13 | expect(isBaseThreadRoute("/threads/environment/thread/")).toBe(true); |
| 14 | expect(isBaseThreadRoute("/threads/environment/thread/files")).toBe(false); |
| 15 | expect(isBaseThreadRoute("/threads/environment/thread/review")).toBe(false); |
| 16 | }); |
| 17 | }); |
| 18 | |
| 19 | describe("resolveThreadSelectionNavigationAction", () => { |
| 20 | it("updates params when a persistent sidebar selects a peer thread", () => { |
| 21 | expect( |
| 22 | resolveThreadSelectionNavigationAction({ |
| 23 | usesSplitView: true, |
| 24 | pathname: "/threads/environment/thread", |
| 25 | }), |
| 26 | ).toBe("set-params"); |
| 27 | }); |
| 28 | |
| 29 | it("replaces nested thread content when a persistent sidebar selects a peer", () => { |
| 30 | expect( |
| 31 | resolveThreadSelectionNavigationAction({ |
| 32 | usesSplitView: true, |
| 33 | pathname: "/threads/environment/thread/files/path", |
| 34 | }), |
| 35 | ).toBe("replace"); |
| 36 | }); |
| 37 | |
| 38 | it("pushes from Home so the back stack survives collapsing to compact", () => { |
| 39 | expect( |
| 40 | resolveThreadSelectionNavigationAction({ |
| 41 | usesSplitView: true, |
| 42 | pathname: "/", |
| 43 | }), |
| 44 | ).toBe("push"); |
| 45 | }); |
| 46 | |
| 47 | it("pushes compact list selections onto the native stack", () => { |
| 48 | expect( |
| 49 | resolveThreadSelectionNavigationAction({ |
| 50 | usesSplitView: false, |
| 51 | pathname: "/threads/environment/thread", |
| 52 | }), |
| 53 | ).toBe("push"); |
| 54 | }); |
| 55 | }); |
| 56 | |
| 57 | describe("resolveFileSelectionNavigationAction", () => { |
| 58 | it("replaces the wide file browser with the selected preview", () => { |
| 59 | expect(resolveFileSelectionNavigationAction({ hasPersistentFileInspector: true })).toBe( |
| 60 | "replace", |
| 61 | ); |
| 62 | }); |
| 63 | |
| 64 | it("pushes a preview above the compact file browser", () => { |
| 65 | expect(resolveFileSelectionNavigationAction({ hasPersistentFileInspector: false })).toBe( |
| 66 | "push", |
| 67 | ); |
| 68 | }); |
| 69 | }); |
| 70 | |
| 71 | describe("resolveThreadSelectionOverlayState", () => { |
| 72 | const stack = { |
| 73 | key: "workspace", |
| 74 | type: "stack", |
| 75 | stale: false as const, |
| 76 | routeNames: ["Home", "Thread", "ThreadFiles", "SettingsSheet", "SettingsLegal"], |
| 77 | }; |
| 78 | const home = { key: "home", name: "Home" }; |
| 79 | const thread = { |
| 80 | key: "thread", |
| 81 | name: "Thread", |
| 82 | params: { environmentId: "environment", threadId: "old-thread" }, |
| 83 | }; |
| 84 | const files = { key: "files", name: "ThreadFiles", params: thread.params }; |
| 85 | const settings = { key: "settings", name: "SettingsSheet" }; |
| 86 | const params = { environmentId: "environment", threadId: "new-thread" }; |
| 87 | |
| 88 | it("replaces the underlying file route and dismisses every overlay above it", () => { |
| 89 | expect( |
| 90 | resolveThreadSelectionOverlayState({ |
| 91 | state: { |
| 92 | ...stack, |
| 93 | index: 4, |
| 94 | routes: [home, thread, files, settings, { key: "legal", name: "SettingsLegal" }], |
| 95 | }, |
| 96 | workspaceRouteKey: files.key, |
| 97 | action: "replace", |
| 98 | params, |
| 99 | }), |
| 100 | ).toEqual({ ...stack, index: 2, routes: [home, thread, { name: "Thread", params }] }); |
| 101 | }); |
| 102 | |
| 103 | it("dismisses an overlay when selecting the current thread without replacing its route key", () => { |
| 104 | expect( |
| 105 | resolveThreadSelectionOverlayState({ |
| 106 | state: { ...stack, index: 2, routes: [home, thread, settings] }, |
| 107 | workspaceRouteKey: thread.key, |
| 108 | action: "set-params", |
| 109 | params: thread.params, |
| 110 | }), |
| 111 | ).toEqual({ ...stack, index: 1, routes: [home, thread] }); |
| 112 | }); |
| 113 | |
| 114 | it.each([home, files])( |
| 115 | "keeps $name in the back stack when pushing from beneath a sheet", |
| 116 | (route) => { |
| 117 | expect( |
| 118 | resolveThreadSelectionOverlayState({ |
| 119 | state: { ...stack, index: 1, routes: [route, settings] }, |
| 120 | workspaceRouteKey: route.key, |
| 121 | action: "push", |
| 122 | params, |
| 123 | }), |
| 124 | ).toEqual({ ...stack, index: 1, routes: [route, { name: "Thread", params }] }); |
| 125 | }, |
| 126 | ); |
| 127 | |
| 128 | it("leaves ordinary thread selection alone when no overlay is present", () => { |
| 129 | expect( |
| 130 | resolveThreadSelectionOverlayState({ |
| 131 | state: { ...stack, index: 2, routes: [home, thread, files] }, |
| 132 | workspaceRouteKey: files.key, |
| 133 | action: "replace", |
| 134 | params, |
| 135 | }), |
| 136 | ).toBeNull(); |
| 137 | }); |
| 138 | }); |