|
1 | 1 | import { describe, expect, it } from "vitest"; |
2 | 2 |
|
3 | 3 | import { |
4 | | - formatThreadJumpHintLabel, |
5 | 4 | getFallbackThreadIdAfterDelete, |
6 | | - getThreadJumpKey, |
| 5 | + getVisibleThreadJumpTargets, |
7 | 6 | getVisibleThreadsForProject, |
8 | 7 | getProjectSortTimestamp, |
9 | 8 | hasUnseenCompletion, |
10 | | - isThreadJumpModifierPressed, |
11 | | - resolveThreadJumpIndex, |
12 | 9 | resolveProjectStatusIndicator, |
13 | 10 | resolveSidebarNewThreadEnvMode, |
14 | 11 | resolveThreadRowClassName, |
@@ -100,94 +97,71 @@ describe("resolveSidebarNewThreadEnvMode", () => { |
100 | 97 | }); |
101 | 98 | }); |
102 | 99 |
|
103 | | -describe("thread jump helpers", () => { |
104 | | - it("assigns jump keys for the first nine visible threads", () => { |
105 | | - expect(getThreadJumpKey(0)).toBe("1"); |
106 | | - expect(getThreadJumpKey(8)).toBe("9"); |
107 | | - expect(getThreadJumpKey(9)).toBeNull(); |
| 100 | +function makeJumpProject( |
| 101 | + expanded: boolean, |
| 102 | + threadIds: ThreadId[], |
| 103 | + shouldShowThreadPanel = expanded, |
| 104 | +) { |
| 105 | + return { |
| 106 | + project: { expanded }, |
| 107 | + renderedThreads: threadIds.map((id) => ({ id })), |
| 108 | + shouldShowThreadPanel, |
| 109 | + }; |
| 110 | +} |
| 111 | + |
| 112 | +describe("getVisibleThreadJumpTargets", () => { |
| 113 | + function tid(n: number): ThreadId { |
| 114 | + return ThreadId.makeUnsafe(`thread-${n}`); |
| 115 | + } |
| 116 | + |
| 117 | + it("returns thread IDs from expanded projects only", () => { |
| 118 | + const targets = getVisibleThreadJumpTargets([ |
| 119 | + makeJumpProject(true, [tid(1), tid(2)]), |
| 120 | + makeJumpProject(false, [tid(3)]), |
| 121 | + makeJumpProject(true, [tid(4)]), |
| 122 | + ]); |
| 123 | + |
| 124 | + expect(targets).toEqual([tid(1), tid(2), tid(4)]); |
108 | 125 | }); |
109 | 126 |
|
110 | | - it("detects the active jump modifier by platform", () => { |
111 | | - expect( |
112 | | - isThreadJumpModifierPressed( |
113 | | - { |
114 | | - key: "Meta", |
115 | | - metaKey: true, |
116 | | - ctrlKey: false, |
117 | | - shiftKey: false, |
118 | | - altKey: false, |
119 | | - }, |
120 | | - "MacIntel", |
121 | | - ), |
122 | | - ).toBe(true); |
123 | | - expect( |
124 | | - isThreadJumpModifierPressed( |
125 | | - { |
126 | | - key: "Control", |
127 | | - metaKey: false, |
128 | | - ctrlKey: true, |
129 | | - shiftKey: false, |
130 | | - altKey: false, |
131 | | - }, |
132 | | - "Win32", |
133 | | - ), |
134 | | - ).toBe(true); |
135 | | - expect( |
136 | | - isThreadJumpModifierPressed( |
137 | | - { |
138 | | - key: "Control", |
139 | | - metaKey: false, |
140 | | - ctrlKey: true, |
141 | | - shiftKey: true, |
142 | | - altKey: false, |
143 | | - }, |
144 | | - "Win32", |
145 | | - ), |
146 | | - ).toBe(false); |
| 127 | + it("skips projects where shouldShowThreadPanel is false", () => { |
| 128 | + const targets = getVisibleThreadJumpTargets([ |
| 129 | + makeJumpProject(true, [tid(1)], false), |
| 130 | + makeJumpProject(true, [tid(2)], true), |
| 131 | + ]); |
| 132 | + |
| 133 | + expect(targets).toEqual([tid(2)]); |
147 | 134 | }); |
148 | 135 |
|
149 | | - it("resolves mod+digit events to zero-based visible thread indices", () => { |
150 | | - expect( |
151 | | - resolveThreadJumpIndex( |
152 | | - { |
153 | | - key: "1", |
154 | | - metaKey: true, |
155 | | - ctrlKey: false, |
156 | | - shiftKey: false, |
157 | | - altKey: false, |
158 | | - }, |
159 | | - "MacIntel", |
160 | | - ), |
161 | | - ).toBe(0); |
162 | | - expect( |
163 | | - resolveThreadJumpIndex( |
164 | | - { |
165 | | - key: "9", |
166 | | - metaKey: false, |
167 | | - ctrlKey: true, |
168 | | - shiftKey: false, |
169 | | - altKey: false, |
170 | | - }, |
171 | | - "Linux", |
172 | | - ), |
173 | | - ).toBe(8); |
174 | | - expect( |
175 | | - resolveThreadJumpIndex( |
176 | | - { |
177 | | - key: "0", |
178 | | - metaKey: false, |
179 | | - ctrlKey: true, |
180 | | - shiftKey: false, |
181 | | - altKey: false, |
182 | | - }, |
183 | | - "Linux", |
184 | | - ), |
185 | | - ).toBeNull(); |
| 136 | + it("caps at 9 targets", () => { |
| 137 | + const allThreads = Array.from({ length: 12 }, (_, i) => tid(i)); |
| 138 | + const targets = getVisibleThreadJumpTargets([makeJumpProject(true, allThreads)]); |
| 139 | + |
| 140 | + expect(targets).toHaveLength(9); |
| 141 | + expect(targets).toEqual(allThreads.slice(0, 9)); |
| 142 | + }); |
| 143 | + |
| 144 | + it("returns empty array when all projects are collapsed", () => { |
| 145 | + const targets = getVisibleThreadJumpTargets([ |
| 146 | + makeJumpProject(false, [tid(1), tid(2)]), |
| 147 | + makeJumpProject(false, [tid(3)]), |
| 148 | + ]); |
| 149 | + |
| 150 | + expect(targets).toEqual([]); |
186 | 151 | }); |
187 | 152 |
|
188 | | - it("formats thread jump hint labels for macOS and non-macOS", () => { |
189 | | - expect(formatThreadJumpHintLabel("3", "MacIntel")).toBe("⌘3"); |
190 | | - expect(formatThreadJumpHintLabel("3", "Linux")).toBe("Ctrl+3"); |
| 153 | + it("returns empty array for no projects", () => { |
| 154 | + expect(getVisibleThreadJumpTargets([])).toEqual([]); |
| 155 | + }); |
| 156 | + |
| 157 | + it("preserves order across multiple expanded projects", () => { |
| 158 | + const targets = getVisibleThreadJumpTargets([ |
| 159 | + makeJumpProject(true, [tid(1), tid(2)]), |
| 160 | + makeJumpProject(true, [tid(3), tid(4)]), |
| 161 | + makeJumpProject(true, [tid(5)]), |
| 162 | + ]); |
| 163 | + |
| 164 | + expect(targets).toEqual([tid(1), tid(2), tid(3), tid(4), tid(5)]); |
191 | 165 | }); |
192 | 166 | }); |
193 | 167 |
|
|
0 commit comments