@google/gemini-cli 0.12.0-nightly.20251023.c4c0c0d1 → 0.12.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/README.md +7 -5
  2. package/dist/package.json +3 -3
  3. package/dist/src/commands/extensions/disable.d.ts +1 -1
  4. package/dist/src/commands/extensions/disable.js +5 -4
  5. package/dist/src/commands/extensions/disable.js.map +1 -1
  6. package/dist/src/commands/extensions/enable.d.ts +1 -1
  7. package/dist/src/commands/extensions/enable.js +3 -2
  8. package/dist/src/commands/extensions/enable.js.map +1 -1
  9. package/dist/src/commands/extensions/install.js +2 -1
  10. package/dist/src/commands/extensions/install.js.map +1 -1
  11. package/dist/src/commands/extensions/install.test.js +1 -0
  12. package/dist/src/commands/extensions/install.test.js.map +1 -1
  13. package/dist/src/commands/extensions/link.js +2 -1
  14. package/dist/src/commands/extensions/link.js.map +1 -1
  15. package/dist/src/commands/extensions/list.js +2 -2
  16. package/dist/src/commands/extensions/list.js.map +1 -1
  17. package/dist/src/commands/extensions/uninstall.js +2 -1
  18. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  19. package/dist/src/commands/extensions/update.js +2 -2
  20. package/dist/src/commands/extensions/update.js.map +1 -1
  21. package/dist/src/commands/mcp/list.js +2 -2
  22. package/dist/src/commands/mcp/list.js.map +1 -1
  23. package/dist/src/config/config.d.ts +6 -3
  24. package/dist/src/config/config.js +56 -11
  25. package/dist/src/config/config.js.map +1 -1
  26. package/dist/src/config/config.test.js +208 -175
  27. package/dist/src/config/config.test.js.map +1 -1
  28. package/dist/src/config/extension-manager.d.ts +23 -10
  29. package/dist/src/config/extension-manager.js +90 -64
  30. package/dist/src/config/extension-manager.js.map +1 -1
  31. package/dist/src/config/extension.test.js +183 -76
  32. package/dist/src/config/extension.test.js.map +1 -1
  33. package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
  34. package/dist/src/config/extensions/extensionEnablement.js +3 -2
  35. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  36. package/dist/src/config/extensions/extensionEnablement.test.js +10 -10
  37. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  38. package/dist/src/config/extensions/extensionSettings.d.ts +3 -3
  39. package/dist/src/config/extensions/extensionSettings.js +74 -24
  40. package/dist/src/config/extensions/extensionSettings.js.map +1 -1
  41. package/dist/src/config/extensions/extensionSettings.test.js +145 -24
  42. package/dist/src/config/extensions/extensionSettings.test.js.map +1 -1
  43. package/dist/src/config/extensions/github.js +3 -3
  44. package/dist/src/config/extensions/github.js.map +1 -1
  45. package/dist/src/config/extensions/github.test.js +1 -1
  46. package/dist/src/config/extensions/github.test.js.map +1 -1
  47. package/dist/src/config/extensions/github_fetch.d.ts +1 -1
  48. package/dist/src/config/extensions/github_fetch.js +13 -1
  49. package/dist/src/config/extensions/github_fetch.js.map +1 -1
  50. package/dist/src/config/extensions/github_fetch.test.d.ts +6 -0
  51. package/dist/src/config/extensions/github_fetch.test.js +169 -0
  52. package/dist/src/config/extensions/github_fetch.test.js.map +1 -0
  53. package/dist/src/config/extensions/update.js +7 -6
  54. package/dist/src/config/extensions/update.js.map +1 -1
  55. package/dist/src/config/extensions/update.test.js +54 -30
  56. package/dist/src/config/extensions/update.test.js.map +1 -1
  57. package/dist/src/config/keyBindings.js +1 -1
  58. package/dist/src/config/keyBindings.js.map +1 -1
  59. package/dist/src/config/policies/read-only.toml +56 -0
  60. package/dist/src/config/policies/write.toml +63 -0
  61. package/dist/src/config/policies/yolo.toml +31 -0
  62. package/dist/src/config/policy-engine.integration.test.js +41 -38
  63. package/dist/src/config/policy-engine.integration.test.js.map +1 -1
  64. package/dist/src/config/policy-toml-loader.d.ts +46 -0
  65. package/dist/src/config/policy-toml-loader.js +314 -0
  66. package/dist/src/config/policy-toml-loader.js.map +1 -0
  67. package/dist/src/config/policy-toml-loader.test.d.ts +6 -0
  68. package/dist/src/config/policy-toml-loader.test.js +626 -0
  69. package/dist/src/config/policy-toml-loader.test.js.map +1 -0
  70. package/dist/src/config/policy.d.ts +9 -2
  71. package/dist/src/config/policy.js +139 -110
  72. package/dist/src/config/policy.js.map +1 -1
  73. package/dist/src/config/policy.test.js +780 -82
  74. package/dist/src/config/policy.test.js.map +1 -1
  75. package/dist/src/config/settings.test.js +6 -6
  76. package/dist/src/config/settings.test.js.map +1 -1
  77. package/dist/src/core/initializer.js +2 -1
  78. package/dist/src/core/initializer.js.map +1 -1
  79. package/dist/src/gemini.js +6 -17
  80. package/dist/src/gemini.js.map +1 -1
  81. package/dist/src/gemini.test.js +27 -2
  82. package/dist/src/gemini.test.js.map +1 -1
  83. package/dist/src/generated/git-commit.d.ts +2 -2
  84. package/dist/src/generated/git-commit.js +2 -2
  85. package/dist/src/generated/git-commit.js.map +1 -1
  86. package/dist/src/nonInteractiveCli.js +16 -4
  87. package/dist/src/nonInteractiveCli.js.map +1 -1
  88. package/dist/src/nonInteractiveCli.test.js +67 -12
  89. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  90. package/dist/src/test-utils/render.d.ts +12 -0
  91. package/dist/src/test-utils/render.js +28 -1
  92. package/dist/src/test-utils/render.js.map +1 -1
  93. package/dist/src/test-utils/render.test.d.ts +6 -0
  94. package/dist/src/test-utils/render.test.js +54 -0
  95. package/dist/src/test-utils/render.test.js.map +1 -0
  96. package/dist/src/ui/AppContainer.js +29 -23
  97. package/dist/src/ui/AppContainer.js.map +1 -1
  98. package/dist/src/ui/AppContainer.test.js +8 -0
  99. package/dist/src/ui/AppContainer.test.js.map +1 -1
  100. package/dist/src/ui/commands/directoryCommand.js +1 -1
  101. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  102. package/dist/src/ui/commands/extensionsCommand.js +45 -1
  103. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  104. package/dist/src/ui/commands/extensionsCommand.test.js +64 -1
  105. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
  106. package/dist/src/ui/commands/memoryCommand.js +1 -1
  107. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  108. package/dist/src/ui/commands/memoryCommand.test.js +3 -1
  109. package/dist/src/ui/commands/memoryCommand.test.js.map +1 -1
  110. package/dist/src/ui/components/ConsoleSummaryDisplay.js +1 -1
  111. package/dist/src/ui/components/ConsoleSummaryDisplay.js.map +1 -1
  112. package/dist/src/ui/components/DetailedMessagesDisplay.js +1 -1
  113. package/dist/src/ui/components/DetailedMessagesDisplay.js.map +1 -1
  114. package/dist/src/ui/components/FolderTrustDialog.test.js +4 -4
  115. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  116. package/dist/src/ui/components/Footer.js +1 -1
  117. package/dist/src/ui/components/Footer.js.map +1 -1
  118. package/dist/src/ui/components/Footer.test.js +24 -0
  119. package/dist/src/ui/components/Footer.test.js.map +1 -1
  120. package/dist/src/ui/components/Help.test.js +0 -1
  121. package/dist/src/ui/components/Help.test.js.map +1 -1
  122. package/dist/src/ui/components/InputPrompt.test.js +442 -342
  123. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  124. package/dist/src/ui/components/ModelDialog.test.js +5 -5
  125. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  126. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +11 -12
  127. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  128. package/dist/src/ui/components/SettingsDialog.test.js +13 -14
  129. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  130. package/dist/src/ui/components/ThemeDialog.test.js +1 -2
  131. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  132. package/dist/src/ui/components/shared/BaseSelectionList.test.js +11 -12
  133. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  134. package/dist/src/ui/components/shared/text-buffer.test.js +2 -1
  135. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  136. package/dist/src/ui/components/views/ExtensionsList.d.ts +1 -1
  137. package/dist/src/ui/components/views/ExtensionsList.js +4 -1
  138. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  139. package/dist/src/ui/contexts/KeypressContext.d.ts +3 -2
  140. package/dist/src/ui/contexts/KeypressContext.js +114 -64
  141. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  142. package/dist/src/ui/contexts/KeypressContext.test.js +166 -482
  143. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  144. package/dist/src/ui/contexts/SessionContext.test.js +27 -13
  145. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  146. package/dist/src/ui/hooks/atCommandProcessor.js +2 -2
  147. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  148. package/dist/src/ui/hooks/shellCommandProcessor.test.js +18 -2
  149. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  150. package/dist/src/ui/hooks/slashCommandProcessor.test.js +74 -80
  151. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
  152. package/dist/src/ui/hooks/useAtCompletion.test.js +32 -23
  153. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
  154. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +2 -1
  155. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  156. package/dist/src/ui/hooks/useCommandCompletion.test.js +79 -78
  157. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
  158. package/dist/src/ui/hooks/useConsoleMessages.test.js +26 -9
  159. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -1
  160. package/dist/src/ui/hooks/useEditorSettings.test.js +40 -34
  161. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  162. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +1 -2
  163. package/dist/src/ui/hooks/useExtensionUpdates.js +4 -2
  164. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  165. package/dist/src/ui/hooks/useExtensionUpdates.test.js +37 -26
  166. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  167. package/dist/src/ui/hooks/useFlickerDetector.test.js +9 -5
  168. package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -1
  169. package/dist/src/ui/hooks/useFocus.test.js +25 -9
  170. package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
  171. package/dist/src/ui/hooks/useFolderTrust.test.js +45 -22
  172. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  173. package/dist/src/ui/hooks/useGeminiStream.js +56 -19
  174. package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
  175. package/dist/src/ui/hooks/useGeminiStream.test.js +155 -74
  176. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  177. package/dist/src/ui/hooks/useGitBranchName.test.js +29 -16
  178. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  179. package/dist/src/ui/hooks/useHistoryManager.test.js +2 -1
  180. package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -1
  181. package/dist/src/ui/hooks/useIdeTrustListener.test.js +24 -7
  182. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
  183. package/dist/src/ui/hooks/useInputHistory.test.js +2 -1
  184. package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -1
  185. package/dist/src/ui/hooks/useInputHistoryStore.test.js +2 -1
  186. package/dist/src/ui/hooks/useInputHistoryStore.test.js.map +1 -1
  187. package/dist/src/ui/hooks/useKeypress.test.js +94 -113
  188. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
  189. package/dist/src/ui/hooks/useLoadingIndicator.test.js +24 -6
  190. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  191. package/dist/src/ui/hooks/useMemoryMonitor.test.js +10 -5
  192. package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
  193. package/dist/src/ui/hooks/useMessageQueue.test.js +61 -45
  194. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  195. package/dist/src/ui/hooks/useModelCommand.test.js +18 -11
  196. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
  197. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +2 -2
  198. package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
  199. package/dist/src/ui/hooks/usePhraseCycler.js +1 -1
  200. package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
  201. package/dist/src/ui/hooks/usePhraseCycler.test.js +83 -110
  202. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
  203. package/dist/src/ui/hooks/usePrivacySettings.test.js +26 -10
  204. package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
  205. package/dist/src/ui/hooks/useQuotaAndFallback.js +13 -14
  206. package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
  207. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +33 -40
  208. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  209. package/dist/src/ui/hooks/useReactToolScheduler.d.ts +8 -1
  210. package/dist/src/ui/hooks/useReactToolScheduler.js +37 -26
  211. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  212. package/dist/src/ui/hooks/useReactToolScheduler.test.js +1 -1
  213. package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -1
  214. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +2 -2
  215. package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -1
  216. package/dist/src/ui/hooks/useSelectionList.test.js +193 -132
  217. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  218. package/dist/src/ui/hooks/useShellHistory.test.js +40 -16
  219. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  220. package/dist/src/ui/hooks/useSlashCompletion.test.js +54 -49
  221. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  222. package/dist/src/ui/hooks/useTimer.test.js +43 -14
  223. package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
  224. package/dist/src/ui/hooks/useToolScheduler.test.js +163 -74
  225. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  226. package/dist/src/ui/hooks/vim.test.js +251 -356
  227. package/dist/src/ui/hooks/vim.test.js.map +1 -1
  228. package/dist/src/ui/keyMatchers.test.js +3 -3
  229. package/dist/src/ui/keyMatchers.test.js.map +1 -1
  230. package/dist/src/ui/utils/textOutput.d.ts +25 -0
  231. package/dist/src/ui/utils/textOutput.js +49 -0
  232. package/dist/src/ui/utils/textOutput.js.map +1 -0
  233. package/dist/src/ui/utils/textOutput.test.d.ts +6 -0
  234. package/dist/src/ui/utils/textOutput.test.js +79 -0
  235. package/dist/src/ui/utils/textOutput.test.js.map +1 -0
  236. package/dist/src/ui/utils/updateCheck.d.ts +7 -1
  237. package/dist/src/ui/utils/updateCheck.js +27 -26
  238. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  239. package/dist/src/ui/utils/updateCheck.test.js +19 -49
  240. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  241. package/dist/src/utils/handleAutoUpdate.js +9 -3
  242. package/dist/src/utils/handleAutoUpdate.js.map +1 -1
  243. package/dist/src/zed-integration/zedIntegration.d.ts +2 -2
  244. package/dist/src/zed-integration/zedIntegration.js +9 -16
  245. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  246. package/dist/tsconfig.tsbuildinfo +1 -1
  247. package/package.json +4 -4
  248. package/dist/google-gemini-cli-0.12.0-nightly.20251022.0542de95.tgz +0 -0
@@ -1,10 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  /**
2
3
  * @license
3
4
  * Copyright 2025 Google LLC
4
5
  * SPDX-License-Identifier: Apache-2.0
5
6
  */
6
7
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
- import { renderHook, act } from '@testing-library/react';
8
+ import { act } from 'react';
9
+ import { render } from 'ink-testing-library';
8
10
  import { useSelectionList, } from './useSelectionList.js';
9
11
  import { useKeypress } from './useKeypress.js';
10
12
  vi.mock('./useKeypress.js');
@@ -49,29 +51,52 @@ describe('useSelectionList', () => {
49
51
  }
50
52
  });
51
53
  };
54
+ const renderSelectionListHook = (initialProps) => {
55
+ let hookResult;
56
+ function TestComponent(props) {
57
+ hookResult = useSelectionList(props);
58
+ return null;
59
+ }
60
+ const { rerender, unmount } = render(_jsx(TestComponent, { ...initialProps }));
61
+ return {
62
+ result: {
63
+ get current() {
64
+ return hookResult;
65
+ },
66
+ },
67
+ rerender: (newProps) => rerender(_jsx(TestComponent, { ...initialProps, ...newProps })),
68
+ unmount,
69
+ };
70
+ };
52
71
  describe('Initialization', () => {
53
72
  it('should initialize with the default index (0) if enabled', () => {
54
- const { result } = renderHook(() => useSelectionList({ items, onSelect: mockOnSelect }));
73
+ const { result } = renderSelectionListHook({
74
+ items,
75
+ onSelect: mockOnSelect,
76
+ });
55
77
  expect(result.current.activeIndex).toBe(0);
56
78
  });
57
79
  it('should initialize with the provided initialIndex if enabled', () => {
58
- const { result } = renderHook(() => useSelectionList({
80
+ const { result } = renderSelectionListHook({
59
81
  items,
60
82
  initialIndex: 2,
61
83
  onSelect: mockOnSelect,
62
- }));
84
+ });
63
85
  expect(result.current.activeIndex).toBe(2);
64
86
  });
65
87
  it('should handle an empty list gracefully', () => {
66
- const { result } = renderHook(() => useSelectionList({ items: [], onSelect: mockOnSelect }));
88
+ const { result } = renderSelectionListHook({
89
+ items: [],
90
+ onSelect: mockOnSelect,
91
+ });
67
92
  expect(result.current.activeIndex).toBe(0);
68
93
  });
69
94
  it('should find the next enabled item (downwards) if initialIndex is disabled', () => {
70
- const { result } = renderHook(() => useSelectionList({
95
+ const { result } = renderSelectionListHook({
71
96
  items,
72
97
  initialIndex: 1,
73
98
  onSelect: mockOnSelect,
74
- }));
99
+ });
75
100
  expect(result.current.activeIndex).toBe(2);
76
101
  });
77
102
  it('should wrap around to find the next enabled item if initialIndex is disabled', () => {
@@ -80,25 +105,25 @@ describe('useSelectionList', () => {
80
105
  { value: 'B', disabled: true, key: 'B' },
81
106
  { value: 'C', disabled: true, key: 'C' },
82
107
  ];
83
- const { result } = renderHook(() => useSelectionList({
108
+ const { result } = renderSelectionListHook({
84
109
  items: wrappingItems,
85
110
  initialIndex: 2,
86
111
  onSelect: mockOnSelect,
87
- }));
112
+ });
88
113
  expect(result.current.activeIndex).toBe(0);
89
114
  });
90
115
  it('should default to 0 if initialIndex is out of bounds', () => {
91
- const { result } = renderHook(() => useSelectionList({
116
+ const { result } = renderSelectionListHook({
92
117
  items,
93
118
  initialIndex: 10,
94
119
  onSelect: mockOnSelect,
95
- }));
120
+ });
96
121
  expect(result.current.activeIndex).toBe(0);
97
- const { result: resultNeg } = renderHook(() => useSelectionList({
122
+ const { result: resultNeg } = renderSelectionListHook({
98
123
  items,
99
124
  initialIndex: -1,
100
125
  onSelect: mockOnSelect,
101
- }));
126
+ });
102
127
  expect(resultNeg.current.activeIndex).toBe(0);
103
128
  });
104
129
  it('should stick to the initial index if all items are disabled', () => {
@@ -106,17 +131,20 @@ describe('useSelectionList', () => {
106
131
  { value: 'A', disabled: true, key: 'A' },
107
132
  { value: 'B', disabled: true, key: 'B' },
108
133
  ];
109
- const { result } = renderHook(() => useSelectionList({
134
+ const { result } = renderSelectionListHook({
110
135
  items: allDisabled,
111
136
  initialIndex: 1,
112
137
  onSelect: mockOnSelect,
113
- }));
138
+ });
114
139
  expect(result.current.activeIndex).toBe(1);
115
140
  });
116
141
  });
117
142
  describe('Keyboard Navigation (Up/Down/J/K)', () => {
118
143
  it('should move down with "j" and "down" keys, skipping disabled items', () => {
119
- const { result } = renderHook(() => useSelectionList({ items, onSelect: mockOnSelect }));
144
+ const { result } = renderSelectionListHook({
145
+ items,
146
+ onSelect: mockOnSelect,
147
+ });
120
148
  expect(result.current.activeIndex).toBe(0);
121
149
  pressKey('j');
122
150
  expect(result.current.activeIndex).toBe(2);
@@ -124,7 +152,11 @@ describe('useSelectionList', () => {
124
152
  expect(result.current.activeIndex).toBe(3);
125
153
  });
126
154
  it('should move up with "k" and "up" keys, skipping disabled items', () => {
127
- const { result } = renderHook(() => useSelectionList({ items, initialIndex: 3, onSelect: mockOnSelect }));
155
+ const { result } = renderSelectionListHook({
156
+ items,
157
+ initialIndex: 3,
158
+ onSelect: mockOnSelect,
159
+ });
128
160
  expect(result.current.activeIndex).toBe(3);
129
161
  pressKey('k');
130
162
  expect(result.current.activeIndex).toBe(2);
@@ -132,11 +164,11 @@ describe('useSelectionList', () => {
132
164
  expect(result.current.activeIndex).toBe(0);
133
165
  });
134
166
  it('should wrap navigation correctly', () => {
135
- const { result } = renderHook(() => useSelectionList({
167
+ const { result } = renderSelectionListHook({
136
168
  items,
137
169
  initialIndex: items.length - 1,
138
170
  onSelect: mockOnSelect,
139
- }));
171
+ });
140
172
  expect(result.current.activeIndex).toBe(3);
141
173
  pressKey('down');
142
174
  expect(result.current.activeIndex).toBe(0);
@@ -144,22 +176,22 @@ describe('useSelectionList', () => {
144
176
  expect(result.current.activeIndex).toBe(3);
145
177
  });
146
178
  it('should call onHighlight when index changes', () => {
147
- renderHook(() => useSelectionList({
179
+ renderSelectionListHook({
148
180
  items,
149
181
  onSelect: mockOnSelect,
150
182
  onHighlight: mockOnHighlight,
151
- }));
183
+ });
152
184
  pressKey('down');
153
185
  expect(mockOnHighlight).toHaveBeenCalledTimes(1);
154
186
  expect(mockOnHighlight).toHaveBeenCalledWith('C');
155
187
  });
156
188
  it('should not move or call onHighlight if navigation results in the same index (e.g., single item)', () => {
157
189
  const singleItem = [{ value: 'A', key: 'A' }];
158
- const { result } = renderHook(() => useSelectionList({
190
+ const { result } = renderSelectionListHook({
159
191
  items: singleItem,
160
192
  onSelect: mockOnSelect,
161
193
  onHighlight: mockOnHighlight,
162
- }));
194
+ });
163
195
  pressKey('down');
164
196
  expect(result.current.activeIndex).toBe(0);
165
197
  expect(mockOnHighlight).not.toHaveBeenCalled();
@@ -169,11 +201,11 @@ describe('useSelectionList', () => {
169
201
  { value: 'A', disabled: true, key: 'A' },
170
202
  { value: 'B', disabled: true, key: 'B' },
171
203
  ];
172
- const { result } = renderHook(() => useSelectionList({
204
+ const { result } = renderSelectionListHook({
173
205
  items: allDisabled,
174
206
  onSelect: mockOnSelect,
175
207
  onHighlight: mockOnHighlight,
176
- }));
208
+ });
177
209
  const initialIndex = result.current.activeIndex;
178
210
  pressKey('down');
179
211
  expect(result.current.activeIndex).toBe(initialIndex);
@@ -182,20 +214,20 @@ describe('useSelectionList', () => {
182
214
  });
183
215
  describe('Selection (Enter)', () => {
184
216
  it('should call onSelect when "return" is pressed on enabled item', () => {
185
- renderHook(() => useSelectionList({
217
+ renderSelectionListHook({
186
218
  items,
187
219
  initialIndex: 2,
188
220
  onSelect: mockOnSelect,
189
- }));
221
+ });
190
222
  pressKey('return');
191
223
  expect(mockOnSelect).toHaveBeenCalledTimes(1);
192
224
  expect(mockOnSelect).toHaveBeenCalledWith('C');
193
225
  });
194
226
  it('should not call onSelect if the active item is disabled', () => {
195
- const { result } = renderHook(() => useSelectionList({
227
+ const { result } = renderSelectionListHook({
196
228
  items,
197
229
  onSelect: mockOnSelect,
198
- }));
230
+ });
199
231
  act(() => result.current.setActiveIndex(1));
200
232
  pressKey('return');
201
233
  expect(mockOnSelect).not.toHaveBeenCalled();
@@ -203,11 +235,11 @@ describe('useSelectionList', () => {
203
235
  });
204
236
  describe('Keyboard Navigation Robustness (Rapid Input)', () => {
205
237
  it('should handle rapid navigation and selection robustly (avoiding stale state)', () => {
206
- const { result } = renderHook(() => useSelectionList({
238
+ const { result } = renderSelectionListHook({
207
239
  items, // A, B(disabled), C, D. Initial index 0 (A).
208
240
  onSelect: mockOnSelect,
209
241
  onHighlight: mockOnHighlight,
210
- }));
242
+ });
211
243
  // Simulate rapid inputs with separate act blocks to allow effects to run
212
244
  if (!activeKeypressHandler)
213
245
  throw new Error('Handler not active');
@@ -244,11 +276,11 @@ describe('useSelectionList', () => {
244
276
  expect(mockOnSelect).not.toHaveBeenCalledWith('A');
245
277
  });
246
278
  it('should handle ultra-rapid input (multiple presses in single act) without stale state', () => {
247
- const { result } = renderHook(() => useSelectionList({
279
+ const { result } = renderSelectionListHook({
248
280
  items, // A, B(disabled), C, D. Initial index 0 (A).
249
281
  onSelect: mockOnSelect,
250
282
  onHighlight: mockOnHighlight,
251
- }));
283
+ });
252
284
  // Simulate ultra-rapid inputs where all keypresses happen faster than React can re-render
253
285
  act(() => {
254
286
  if (!activeKeypressHandler)
@@ -278,27 +310,38 @@ describe('useSelectionList', () => {
278
310
  });
279
311
  describe('Focus Management (isFocused)', () => {
280
312
  it('should activate the keypress handler when focused (default) and items exist', () => {
281
- const { result } = renderHook(() => useSelectionList({ items, onSelect: mockOnSelect }));
313
+ const { result } = renderSelectionListHook({
314
+ items,
315
+ onSelect: mockOnSelect,
316
+ });
282
317
  expect(activeKeypressHandler).not.toBeNull();
283
318
  pressKey('down');
284
319
  expect(result.current.activeIndex).toBe(2);
285
320
  });
286
321
  it('should not activate the keypress handler when isFocused is false', () => {
287
- renderHook(() => useSelectionList({ items, onSelect: mockOnSelect, isFocused: false }));
322
+ renderSelectionListHook({
323
+ items,
324
+ onSelect: mockOnSelect,
325
+ isFocused: false,
326
+ });
288
327
  expect(activeKeypressHandler).toBeNull();
289
328
  expect(() => pressKey('down')).toThrow(/keypress handler is not active/);
290
329
  });
291
330
  it('should not activate the keypress handler when items list is empty', () => {
292
- renderHook(() => useSelectionList({
331
+ renderSelectionListHook({
293
332
  items: [],
294
333
  onSelect: mockOnSelect,
295
334
  isFocused: true,
296
- }));
335
+ });
297
336
  expect(activeKeypressHandler).toBeNull();
298
337
  expect(() => pressKey('down')).toThrow(/keypress handler is not active/);
299
338
  });
300
339
  it('should activate/deactivate when isFocused prop changes', () => {
301
- const { result, rerender } = renderHook((props) => useSelectionList({ items, onSelect: mockOnSelect, ...props }), { initialProps: { isFocused: false } });
340
+ const { result, rerender } = renderSelectionListHook({
341
+ items,
342
+ onSelect: mockOnSelect,
343
+ isFocused: false,
344
+ });
302
345
  expect(activeKeypressHandler).toBeNull();
303
346
  rerender({ isFocused: true });
304
347
  expect(activeKeypressHandler).not.toBeNull();
@@ -320,18 +363,21 @@ describe('useSelectionList', () => {
320
363
  const longList = Array.from({ length: 15 }, (_, i) => ({ value: `Item ${i + 1}`, key: `Item ${i + 1}` }));
321
364
  const pressNumber = (num) => pressKey(num, num);
322
365
  it('should not respond to numbers if showNumbers is false (default)', () => {
323
- const { result } = renderHook(() => useSelectionList({ items: shortList, onSelect: mockOnSelect }));
366
+ const { result } = renderSelectionListHook({
367
+ items: shortList,
368
+ onSelect: mockOnSelect,
369
+ });
324
370
  pressNumber('1');
325
371
  expect(result.current.activeIndex).toBe(0);
326
372
  expect(mockOnSelect).not.toHaveBeenCalled();
327
373
  });
328
374
  it('should select item immediately if the number cannot be extended (unambiguous)', () => {
329
- const { result } = renderHook(() => useSelectionList({
375
+ const { result } = renderSelectionListHook({
330
376
  items: shortList,
331
377
  onSelect: mockOnSelect,
332
378
  onHighlight: mockOnHighlight,
333
379
  showNumbers: true,
334
- }));
380
+ });
335
381
  pressNumber('3');
336
382
  expect(result.current.activeIndex).toBe(2);
337
383
  expect(mockOnHighlight).toHaveBeenCalledWith('C');
@@ -340,13 +386,13 @@ describe('useSelectionList', () => {
340
386
  expect(vi.getTimerCount()).toBe(0);
341
387
  });
342
388
  it('should highlight and wait for timeout if the number can be extended (ambiguous)', () => {
343
- const { result } = renderHook(() => useSelectionList({
389
+ const { result } = renderSelectionListHook({
344
390
  items: longList,
345
391
  initialIndex: 1, // Start at index 1 so pressing "1" (index 0) causes a change
346
392
  onSelect: mockOnSelect,
347
393
  onHighlight: mockOnHighlight,
348
394
  showNumbers: true,
349
- }));
395
+ });
350
396
  pressNumber('1');
351
397
  expect(result.current.activeIndex).toBe(0);
352
398
  expect(mockOnHighlight).toHaveBeenCalledWith('Item 1');
@@ -359,11 +405,11 @@ describe('useSelectionList', () => {
359
405
  expect(mockOnSelect).toHaveBeenCalledWith('Item 1');
360
406
  });
361
407
  it('should handle multi-digit input correctly', () => {
362
- const { result } = renderHook(() => useSelectionList({
408
+ const { result } = renderSelectionListHook({
363
409
  items: longList,
364
410
  onSelect: mockOnSelect,
365
411
  showNumbers: true,
366
- }));
412
+ });
367
413
  pressNumber('1');
368
414
  expect(mockOnSelect).not.toHaveBeenCalled();
369
415
  pressNumber('2');
@@ -372,11 +418,11 @@ describe('useSelectionList', () => {
372
418
  expect(mockOnSelect).toHaveBeenCalledWith('Item 12');
373
419
  });
374
420
  it('should reset buffer if input becomes invalid (out of bounds)', () => {
375
- const { result } = renderHook(() => useSelectionList({
421
+ const { result } = renderSelectionListHook({
376
422
  items: shortList,
377
423
  onSelect: mockOnSelect,
378
424
  showNumbers: true,
379
- }));
425
+ });
380
426
  pressNumber('5');
381
427
  expect(result.current.activeIndex).toBe(0);
382
428
  expect(mockOnSelect).not.toHaveBeenCalled();
@@ -385,11 +431,11 @@ describe('useSelectionList', () => {
385
431
  expect(mockOnSelect).toHaveBeenCalledWith('C');
386
432
  });
387
433
  it('should allow "0" as subsequent digit, but ignore as first digit', () => {
388
- const { result } = renderHook(() => useSelectionList({
434
+ const { result } = renderSelectionListHook({
389
435
  items: longList,
390
436
  onSelect: mockOnSelect,
391
437
  showNumbers: true,
392
- }));
438
+ });
393
439
  pressNumber('0');
394
440
  expect(result.current.activeIndex).toBe(0);
395
441
  expect(mockOnSelect).not.toHaveBeenCalled();
@@ -402,11 +448,11 @@ describe('useSelectionList', () => {
402
448
  expect(mockOnSelect).toHaveBeenCalledWith('Item 10');
403
449
  });
404
450
  it('should clear the initial "0" input after timeout', () => {
405
- renderHook(() => useSelectionList({
451
+ renderSelectionListHook({
406
452
  items: longList,
407
453
  onSelect: mockOnSelect,
408
454
  showNumbers: true,
409
- }));
455
+ });
410
456
  pressNumber('0');
411
457
  act(() => vi.advanceTimersByTime(1000)); // Timeout the '0' input
412
458
  pressNumber('1');
@@ -415,12 +461,12 @@ describe('useSelectionList', () => {
415
461
  expect(mockOnSelect).toHaveBeenCalledWith('Item 1');
416
462
  });
417
463
  it('should highlight but not select a disabled item (immediate selection case)', () => {
418
- const { result } = renderHook(() => useSelectionList({
464
+ const { result } = renderSelectionListHook({
419
465
  items: shortList, // B (index 1, number 2) is disabled
420
466
  onSelect: mockOnSelect,
421
467
  onHighlight: mockOnHighlight,
422
468
  showNumbers: true,
423
- }));
469
+ });
424
470
  pressNumber('2');
425
471
  expect(result.current.activeIndex).toBe(1);
426
472
  expect(mockOnHighlight).toHaveBeenCalledWith('B');
@@ -433,11 +479,11 @@ describe('useSelectionList', () => {
433
479
  { value: 'Item 1 Disabled', disabled: true, key: 'Item 1 Disabled' },
434
480
  ...longList.slice(1),
435
481
  ];
436
- const { result } = renderHook(() => useSelectionList({
482
+ const { result } = renderSelectionListHook({
437
483
  items: disabledAmbiguousList,
438
484
  onSelect: mockOnSelect,
439
485
  showNumbers: true,
440
- }));
486
+ });
441
487
  pressNumber('1');
442
488
  expect(result.current.activeIndex).toBe(0);
443
489
  expect(vi.getTimerCount()).toBe(1);
@@ -448,11 +494,11 @@ describe('useSelectionList', () => {
448
494
  expect(mockOnSelect).not.toHaveBeenCalled();
449
495
  });
450
496
  it('should clear the number buffer if a non-numeric key (e.g., navigation) is pressed', () => {
451
- const { result } = renderHook(() => useSelectionList({
497
+ const { result } = renderSelectionListHook({
452
498
  items: longList,
453
499
  onSelect: mockOnSelect,
454
500
  showNumbers: true,
455
- }));
501
+ });
456
502
  pressNumber('1');
457
503
  expect(vi.getTimerCount()).toBe(1);
458
504
  pressKey('down');
@@ -463,11 +509,11 @@ describe('useSelectionList', () => {
463
509
  expect(result.current.activeIndex).toBe(2);
464
510
  });
465
511
  it('should clear the number buffer if "return" is pressed', () => {
466
- renderHook(() => useSelectionList({
512
+ renderSelectionListHook({
467
513
  items: longList,
468
514
  onSelect: mockOnSelect,
469
515
  showNumbers: true,
470
- }));
516
+ });
471
517
  pressNumber('1');
472
518
  pressKey('return');
473
519
  expect(mockOnSelect).toHaveBeenCalledTimes(1);
@@ -479,44 +525,50 @@ describe('useSelectionList', () => {
479
525
  });
480
526
  });
481
527
  describe('Reactivity (Dynamic Updates)', () => {
482
- it('should update activeIndex when initialIndex prop changes', () => {
483
- const { result, rerender } = renderHook(({ initialIndex }) => useSelectionList({
528
+ it('should update activeIndex when initialIndex prop changes', async () => {
529
+ const { result, rerender } = renderSelectionListHook({
484
530
  items,
485
531
  onSelect: mockOnSelect,
486
- initialIndex,
487
- }), { initialProps: { initialIndex: 0 } });
532
+ initialIndex: 0,
533
+ });
488
534
  rerender({ initialIndex: 2 });
489
- expect(result.current.activeIndex).toBe(2);
535
+ await vi.waitFor(() => {
536
+ expect(result.current.activeIndex).toBe(2);
537
+ });
490
538
  });
491
- it('should respect a new initialIndex even after user interaction', () => {
492
- const { result, rerender } = renderHook(({ initialIndex }) => useSelectionList({
539
+ it('should respect a new initialIndex even after user interaction', async () => {
540
+ const { result, rerender } = renderSelectionListHook({
493
541
  items,
494
542
  onSelect: mockOnSelect,
495
- initialIndex,
496
- }), { initialProps: { initialIndex: 0 } });
543
+ initialIndex: 0,
544
+ });
497
545
  // User navigates, changing the active index
498
546
  pressKey('down');
499
547
  expect(result.current.activeIndex).toBe(2);
500
548
  // The component re-renders with a new initial index
501
549
  rerender({ initialIndex: 3 });
502
550
  // The hook should now respect the new initial index
503
- expect(result.current.activeIndex).toBe(3);
551
+ await vi.waitFor(() => {
552
+ expect(result.current.activeIndex).toBe(3);
553
+ });
504
554
  });
505
- it('should validate index when initialIndex prop changes to a disabled item', () => {
506
- const { result, rerender } = renderHook(({ initialIndex }) => useSelectionList({
555
+ it('should validate index when initialIndex prop changes to a disabled item', async () => {
556
+ const { result, rerender } = renderSelectionListHook({
507
557
  items,
508
558
  onSelect: mockOnSelect,
509
- initialIndex,
510
- }), { initialProps: { initialIndex: 0 } });
559
+ initialIndex: 0,
560
+ });
511
561
  rerender({ initialIndex: 1 });
512
- expect(result.current.activeIndex).toBe(2);
562
+ await vi.waitFor(() => {
563
+ expect(result.current.activeIndex).toBe(2);
564
+ });
513
565
  });
514
- it('should adjust activeIndex if items change and the initialIndex is now out of bounds', () => {
515
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
566
+ it('should adjust activeIndex if items change and the initialIndex is now out of bounds', async () => {
567
+ const { result, rerender } = renderSelectionListHook({
516
568
  onSelect: mockOnSelect,
517
569
  initialIndex: 3,
518
- items: testItems,
519
- }), { initialProps: { items } });
570
+ items,
571
+ });
520
572
  expect(result.current.activeIndex).toBe(3);
521
573
  const shorterItems = [
522
574
  { value: 'X', key: 'X' },
@@ -524,19 +576,21 @@ describe('useSelectionList', () => {
524
576
  ];
525
577
  rerender({ items: shorterItems }); // Length 2
526
578
  // The useEffect syncs based on the initialIndex (3) which is now out of bounds. It defaults to 0.
527
- expect(result.current.activeIndex).toBe(0);
579
+ await vi.waitFor(() => {
580
+ expect(result.current.activeIndex).toBe(0);
581
+ });
528
582
  });
529
- it('should adjust activeIndex if items change and the initialIndex becomes disabled', () => {
583
+ it('should adjust activeIndex if items change and the initialIndex becomes disabled', async () => {
530
584
  const initialItems = [
531
585
  { value: 'A', key: 'A' },
532
586
  { value: 'B', key: 'B' },
533
587
  { value: 'C', key: 'C' },
534
588
  ];
535
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
589
+ const { result, rerender } = renderSelectionListHook({
536
590
  onSelect: mockOnSelect,
537
591
  initialIndex: 1,
538
- items: testItems,
539
- }), { initialProps: { items: initialItems } });
592
+ items: initialItems,
593
+ });
540
594
  expect(result.current.activeIndex).toBe(1);
541
595
  const newItems = [
542
596
  { value: 'A', key: 'A' },
@@ -544,30 +598,34 @@ describe('useSelectionList', () => {
544
598
  { value: 'C', key: 'C' },
545
599
  ];
546
600
  rerender({ items: newItems });
547
- expect(result.current.activeIndex).toBe(2);
601
+ await vi.waitFor(() => {
602
+ expect(result.current.activeIndex).toBe(2);
603
+ });
548
604
  });
549
- it('should reset to 0 if items change to an empty list', () => {
550
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
605
+ it('should reset to 0 if items change to an empty list', async () => {
606
+ const { result, rerender } = renderSelectionListHook({
551
607
  onSelect: mockOnSelect,
552
608
  initialIndex: 2,
553
- items: testItems,
554
- }), { initialProps: { items } });
609
+ items,
610
+ });
555
611
  rerender({ items: [] });
556
- expect(result.current.activeIndex).toBe(0);
612
+ await vi.waitFor(() => {
613
+ expect(result.current.activeIndex).toBe(0);
614
+ });
557
615
  });
558
- it('should not reset activeIndex when items are deeply equal', () => {
616
+ it('should not reset activeIndex when items are deeply equal', async () => {
559
617
  const initialItems = [
560
618
  { value: 'A', key: 'A' },
561
619
  { value: 'B', disabled: true, key: 'B' },
562
620
  { value: 'C', key: 'C' },
563
621
  { value: 'D', key: 'D' },
564
622
  ];
565
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
623
+ const { result, rerender } = renderSelectionListHook({
566
624
  onSelect: mockOnSelect,
567
625
  onHighlight: mockOnHighlight,
568
626
  initialIndex: 2,
569
- items: testItems,
570
- }), { initialProps: { items: initialItems } });
627
+ items: initialItems,
628
+ });
571
629
  expect(result.current.activeIndex).toBe(2);
572
630
  act(() => {
573
631
  result.current.setActiveIndex(3);
@@ -583,23 +641,25 @@ describe('useSelectionList', () => {
583
641
  ];
584
642
  rerender({ items: newItems });
585
643
  // Active index should remain the same since items are deeply equal
586
- expect(result.current.activeIndex).toBe(3);
644
+ await vi.waitFor(() => {
645
+ expect(result.current.activeIndex).toBe(3);
646
+ });
587
647
  // onHighlight should NOT be called since the index didn't change
588
648
  expect(mockOnHighlight).not.toHaveBeenCalled();
589
649
  });
590
- it('should update activeIndex when items change structurally', () => {
650
+ it('should update activeIndex when items change structurally', async () => {
591
651
  const initialItems = [
592
652
  { value: 'A', key: 'A' },
593
653
  { value: 'B', disabled: true, key: 'B' },
594
654
  { value: 'C', key: 'C' },
595
655
  { value: 'D', key: 'D' },
596
656
  ];
597
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
657
+ const { result, rerender } = renderSelectionListHook({
598
658
  onSelect: mockOnSelect,
599
659
  onHighlight: mockOnHighlight,
600
660
  initialIndex: 3,
601
- items: testItems,
602
- }), { initialProps: { items: initialItems } });
661
+ items: initialItems,
662
+ });
603
663
  expect(result.current.activeIndex).toBe(3);
604
664
  mockOnHighlight.mockClear();
605
665
  // Change item values (not deeply equal)
@@ -610,19 +670,21 @@ describe('useSelectionList', () => {
610
670
  ];
611
671
  rerender({ items: newItems });
612
672
  // Active index should update based on initialIndex and new items
613
- expect(result.current.activeIndex).toBe(0);
673
+ await vi.waitFor(() => {
674
+ expect(result.current.activeIndex).toBe(0);
675
+ });
614
676
  });
615
- it('should handle partial changes in items array', () => {
677
+ it('should handle partial changes in items array', async () => {
616
678
  const initialItems = [
617
679
  { value: 'A', key: 'A' },
618
680
  { value: 'B', key: 'B' },
619
681
  { value: 'C', key: 'C' },
620
682
  ];
621
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
683
+ const { result, rerender } = renderSelectionListHook({
622
684
  onSelect: mockOnSelect,
623
685
  initialIndex: 1,
624
- items: testItems,
625
- }), { initialProps: { items: initialItems } });
686
+ items: initialItems,
687
+ });
626
688
  expect(result.current.activeIndex).toBe(1);
627
689
  // Change only one item's disabled status
628
690
  const newItems = [
@@ -632,18 +694,20 @@ describe('useSelectionList', () => {
632
694
  ];
633
695
  rerender({ items: newItems });
634
696
  // Should find next valid index since current became disabled
635
- expect(result.current.activeIndex).toBe(2);
697
+ await vi.waitFor(() => {
698
+ expect(result.current.activeIndex).toBe(2);
699
+ });
636
700
  });
637
- it('should update selection when a new item is added to the start of the list', () => {
701
+ it('should update selection when a new item is added to the start of the list', async () => {
638
702
  const initialItems = [
639
703
  { value: 'A', key: 'A' },
640
704
  { value: 'B', key: 'B' },
641
705
  { value: 'C', key: 'C' },
642
706
  ];
643
- const { result, rerender } = renderHook(({ items: testItems }) => useSelectionList({
707
+ const { result, rerender } = renderSelectionListHook({
644
708
  onSelect: mockOnSelect,
645
- items: testItems,
646
- }), { initialProps: { items: initialItems } });
709
+ items: initialItems,
710
+ });
647
711
  pressKey('down');
648
712
  expect(result.current.activeIndex).toBe(1);
649
713
  const newItems = [
@@ -653,7 +717,9 @@ describe('useSelectionList', () => {
653
717
  { value: 'C', key: 'C' },
654
718
  ];
655
719
  rerender({ items: newItems });
656
- expect(result.current.activeIndex).toBe(2);
720
+ await vi.waitFor(() => {
721
+ expect(result.current.activeIndex).toBe(2);
722
+ });
657
723
  });
658
724
  it('should not re-initialize when items have identical keys but are different objects', () => {
659
725
  const initialItems = [
@@ -661,14 +727,22 @@ describe('useSelectionList', () => {
661
727
  { value: 'B', key: 'B' },
662
728
  ];
663
729
  let renderCount = 0;
664
- const { rerender } = renderHook(({ items: testItems }) => {
665
- renderCount++;
666
- return useSelectionList({
667
- onSelect: mockOnSelect,
668
- onHighlight: mockOnHighlight,
669
- items: testItems,
670
- });
671
- }, { initialProps: { items: initialItems } });
730
+ const renderHookWithCount = (initialProps) => {
731
+ function TestComponent(props) {
732
+ renderCount++;
733
+ useSelectionList({
734
+ onSelect: mockOnSelect,
735
+ onHighlight: mockOnHighlight,
736
+ items: props.items,
737
+ });
738
+ return null;
739
+ }
740
+ const { rerender } = render(_jsx(TestComponent, { ...initialProps }));
741
+ return {
742
+ rerender: (newProps) => rerender(_jsx(TestComponent, { ...initialProps, ...newProps })),
743
+ };
744
+ };
745
+ const { rerender } = renderHookWithCount({ items: initialItems });
672
746
  // Initial render
673
747
  expect(renderCount).toBe(1);
674
748
  // Create new items with the same keys but different object references
@@ -680,19 +754,6 @@ describe('useSelectionList', () => {
680
754
  expect(renderCount).toBe(2);
681
755
  });
682
756
  });
683
- describe('Manual Control', () => {
684
- it('should allow manual setting of active index via setActiveIndex', () => {
685
- const { result } = renderHook(() => useSelectionList({ items, onSelect: mockOnSelect }));
686
- act(() => {
687
- result.current.setActiveIndex(3);
688
- });
689
- expect(result.current.activeIndex).toBe(3);
690
- act(() => {
691
- result.current.setActiveIndex(1);
692
- });
693
- expect(result.current.activeIndex).toBe(1);
694
- });
695
- });
696
757
  describe('Cleanup', () => {
697
758
  beforeEach(() => {
698
759
  vi.useFakeTimers();
@@ -702,11 +763,11 @@ describe('useSelectionList', () => {
702
763
  });
703
764
  it('should clear timeout on unmount when timer is active', () => {
704
765
  const longList = Array.from({ length: 15 }, (_, i) => ({ value: `Item ${i + 1}`, key: `Item ${i + 1}` }));
705
- const { unmount } = renderHook(() => useSelectionList({
766
+ const { unmount } = renderSelectionListHook({
706
767
  items: longList,
707
768
  onSelect: mockOnSelect,
708
769
  showNumbers: true,
709
- }));
770
+ });
710
771
  pressKey('1', '1');
711
772
  expect(vi.getTimerCount()).toBe(1);
712
773
  act(() => {