@google/gemini-cli 0.12.0-preview.4 → 0.13.0-nightly.20251031.c89bc30d

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 (254) hide show
  1. package/dist/google-gemini-cli-0.13.0-nightly.20251029.cca41edc.tgz +0 -0
  2. package/dist/package.json +2 -2
  3. package/dist/src/commands/extensions/install.js +2 -2
  4. package/dist/src/commands/extensions/install.js.map +1 -1
  5. package/dist/src/commands/extensions/install.test.js +27 -16
  6. package/dist/src/commands/extensions/install.test.js.map +1 -1
  7. package/dist/src/commands/extensions/link.js +2 -2
  8. package/dist/src/commands/extensions/link.js.map +1 -1
  9. package/dist/src/commands/extensions/update.js +3 -2
  10. package/dist/src/commands/extensions/update.js.map +1 -1
  11. package/dist/src/commands/extensions/validate.d.ts +12 -0
  12. package/dist/src/commands/extensions/validate.js +83 -0
  13. package/dist/src/commands/extensions/validate.js.map +1 -0
  14. package/dist/src/commands/extensions/validate.test.d.ts +6 -0
  15. package/dist/src/commands/extensions/validate.test.js +93 -0
  16. package/dist/src/commands/extensions/validate.test.js.map +1 -0
  17. package/dist/src/commands/extensions.js +2 -0
  18. package/dist/src/commands/extensions.js.map +1 -1
  19. package/dist/src/config/auth.js +0 -5
  20. package/dist/src/config/auth.js.map +1 -1
  21. package/dist/src/config/auth.test.js +1 -3
  22. package/dist/src/config/auth.test.js.map +1 -1
  23. package/dist/src/config/config.js +2 -1
  24. package/dist/src/config/config.js.map +1 -1
  25. package/dist/src/config/config.test.js +6 -10
  26. package/dist/src/config/config.test.js.map +1 -1
  27. package/dist/src/config/extensions/update.d.ts +2 -2
  28. package/dist/src/config/extensions/update.js +28 -22
  29. package/dist/src/config/extensions/update.js.map +1 -1
  30. package/dist/src/config/sandboxConfig.d.ts +1 -1
  31. package/dist/src/config/sandboxConfig.js +6 -3
  32. package/dist/src/config/sandboxConfig.js.map +1 -1
  33. package/dist/src/config/settings.js +2 -2
  34. package/dist/src/config/settings.js.map +1 -1
  35. package/dist/src/config/settings.test.js +13 -53
  36. package/dist/src/config/settings.test.js.map +1 -1
  37. package/dist/src/config/settingsSchema.d.ts +25 -7
  38. package/dist/src/config/settingsSchema.js +24 -6
  39. package/dist/src/config/settingsSchema.js.map +1 -1
  40. package/dist/src/config/settingsSchema.test.js +2 -0
  41. package/dist/src/config/settingsSchema.test.js.map +1 -1
  42. package/dist/src/gemini.js +8 -1
  43. package/dist/src/gemini.js.map +1 -1
  44. package/dist/src/gemini.test.js +1 -0
  45. package/dist/src/gemini.test.js.map +1 -1
  46. package/dist/src/generated/git-commit.d.ts +2 -2
  47. package/dist/src/generated/git-commit.js +2 -2
  48. package/dist/src/generated/git-commit.js.map +1 -1
  49. package/dist/src/nonInteractiveCli.d.ts +9 -1
  50. package/dist/src/nonInteractiveCli.js +16 -1
  51. package/dist/src/nonInteractiveCli.js.map +1 -1
  52. package/dist/src/nonInteractiveCli.test.js +209 -103
  53. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  54. package/dist/src/test-utils/async.d.ts +9 -0
  55. package/dist/src/test-utils/async.js +29 -0
  56. package/dist/src/test-utils/async.js.map +1 -0
  57. package/dist/src/test-utils/render.d.ts +2 -1
  58. package/dist/src/test-utils/render.js +23 -1
  59. package/dist/src/test-utils/render.js.map +1 -1
  60. package/dist/src/test-utils/render.test.js +29 -4
  61. package/dist/src/test-utils/render.test.js.map +1 -1
  62. package/dist/src/ui/App.test.js +1 -1
  63. package/dist/src/ui/App.test.js.map +1 -1
  64. package/dist/src/ui/AppContainer.js +33 -4
  65. package/dist/src/ui/AppContainer.js.map +1 -1
  66. package/dist/src/ui/AppContainer.test.js +205 -99
  67. package/dist/src/ui/AppContainer.test.js.map +1 -1
  68. package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
  69. package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
  70. package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
  71. package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
  72. package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
  73. package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
  74. package/dist/src/ui/auth/AuthDialog.js +7 -3
  75. package/dist/src/ui/auth/AuthDialog.js.map +1 -1
  76. package/dist/src/ui/auth/AuthDialog.test.js +1 -1
  77. package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
  78. package/dist/src/ui/auth/useAuth.d.ts +2 -0
  79. package/dist/src/ui/auth/useAuth.js +31 -2
  80. package/dist/src/ui/auth/useAuth.js.map +1 -1
  81. package/dist/src/ui/components/AnsiOutput.test.js +1 -1
  82. package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
  83. package/dist/src/ui/components/Composer.test.js +1 -1
  84. package/dist/src/ui/components/Composer.test.js.map +1 -1
  85. package/dist/src/ui/components/ConsentPrompt.test.js +18 -8
  86. package/dist/src/ui/components/ConsentPrompt.test.js.map +1 -1
  87. package/dist/src/ui/components/ContextSummaryDisplay.test.js +11 -6
  88. package/dist/src/ui/components/ContextSummaryDisplay.test.js.map +1 -1
  89. package/dist/src/ui/components/DialogManager.js +4 -0
  90. package/dist/src/ui/components/DialogManager.js.map +1 -1
  91. package/dist/src/ui/components/FolderTrustDialog.test.js +4 -3
  92. package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
  93. package/dist/src/ui/components/Footer.js +3 -2
  94. package/dist/src/ui/components/Footer.js.map +1 -1
  95. package/dist/src/ui/components/Footer.test.js +59 -0
  96. package/dist/src/ui/components/Footer.test.js.map +1 -1
  97. package/dist/src/ui/components/Header.test.js +9 -5
  98. package/dist/src/ui/components/Header.test.js.map +1 -1
  99. package/dist/src/ui/components/Help.test.js +5 -3
  100. package/dist/src/ui/components/Help.test.js.map +1 -1
  101. package/dist/src/ui/components/InputPrompt.test.js +139 -111
  102. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  103. package/dist/src/ui/components/LoadingIndicator.test.js +27 -14
  104. package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
  105. package/dist/src/ui/components/ModelDialog.test.js +20 -10
  106. package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
  107. package/dist/src/ui/components/ModelStatsDisplay.test.js +1 -1
  108. package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -1
  109. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +11 -10
  110. package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
  111. package/dist/src/ui/components/PrepareLabel.test.js +13 -7
  112. package/dist/src/ui/components/PrepareLabel.test.js.map +1 -1
  113. package/dist/src/ui/components/ProQuotaDialog.test.js +14 -6
  114. package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
  115. package/dist/src/ui/components/QueuedMessageDisplay.test.js +11 -6
  116. package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
  117. package/dist/src/ui/components/SessionSummaryDisplay.test.js +1 -1
  118. package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
  119. package/dist/src/ui/components/SettingsDialog.test.js +438 -512
  120. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  121. package/dist/src/ui/components/StatsDisplay.test.js +1 -1
  122. package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
  123. package/dist/src/ui/components/ThemeDialog.test.js +3 -2
  124. package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
  125. package/dist/src/ui/components/ToolStatsDisplay.test.js +1 -1
  126. package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -1
  127. package/dist/src/ui/components/messages/CompressionMessage.test.js +25 -17
  128. package/dist/src/ui/components/messages/CompressionMessage.test.js.map +1 -1
  129. package/dist/src/ui/components/messages/DiffRenderer.test.js +1 -1
  130. package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
  131. package/dist/src/ui/components/messages/Todo.js +27 -5
  132. package/dist/src/ui/components/messages/Todo.js.map +1 -1
  133. package/dist/src/ui/components/messages/Todo.test.js +20 -8
  134. package/dist/src/ui/components/messages/Todo.test.js.map +1 -1
  135. package/dist/src/ui/components/messages/ToolGroupMessage.test.js +29 -15
  136. package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
  137. package/dist/src/ui/components/shared/BaseSelectionList.test.js +12 -11
  138. package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
  139. package/dist/src/ui/components/shared/MaxSizedBox.test.js +43 -22
  140. package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -1
  141. package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
  142. package/dist/src/ui/components/shared/TextInput.js +38 -0
  143. package/dist/src/ui/components/shared/TextInput.js.map +1 -0
  144. package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
  145. package/dist/src/ui/components/shared/TextInput.test.js +242 -0
  146. package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
  147. package/dist/src/ui/components/shared/text-buffer.d.ts +8 -2
  148. package/dist/src/ui/components/shared/text-buffer.js +28 -13
  149. package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
  150. package/dist/src/ui/components/shared/text-buffer.test.js +137 -0
  151. package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
  152. package/dist/src/ui/components/views/ChatList.test.js +7 -4
  153. package/dist/src/ui/components/views/ChatList.test.js.map +1 -1
  154. package/dist/src/ui/components/views/ExtensionsList.js +1 -0
  155. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  156. package/dist/src/ui/components/views/ExtensionsList.test.js +13 -5
  157. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  158. package/dist/src/ui/components/views/McpStatus.test.js +23 -12
  159. package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
  160. package/dist/src/ui/contexts/KeypressContext.test.js +132 -252
  161. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  162. package/dist/src/ui/contexts/SessionContext.test.js +9 -5
  163. package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
  164. package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
  165. package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
  166. package/dist/src/ui/contexts/UIStateContext.d.ts +2 -0
  167. package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
  168. package/dist/src/ui/hooks/atCommandProcessor.js +29 -7
  169. package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
  170. package/dist/src/ui/hooks/atCommandProcessor.test.js +163 -64
  171. package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
  172. package/dist/src/ui/hooks/shellCommandProcessor.test.js +47 -34
  173. package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
  174. package/dist/src/ui/hooks/slashCommandProcessor.test.js +141 -104
  175. package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
  176. package/dist/src/ui/hooks/useAtCompletion.test.js +23 -21
  177. package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
  178. package/dist/src/ui/hooks/useCommandCompletion.test.js +16 -15
  179. package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
  180. package/dist/src/ui/hooks/useConsoleMessages.test.js +1 -1
  181. package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -1
  182. package/dist/src/ui/hooks/useEditorSettings.test.js +1 -1
  183. package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
  184. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +1 -1
  185. package/dist/src/ui/hooks/useExtensionUpdates.js +9 -3
  186. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  187. package/dist/src/ui/hooks/useExtensionUpdates.test.js +10 -9
  188. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  189. package/dist/src/ui/hooks/useFocus.test.js +1 -1
  190. package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
  191. package/dist/src/ui/hooks/useFolderTrust.test.js +7 -6
  192. package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
  193. package/dist/src/ui/hooks/useGeminiStream.test.js +39 -38
  194. package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
  195. package/dist/src/ui/hooks/useGitBranchName.test.js +5 -4
  196. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  197. package/dist/src/ui/hooks/useIdeTrustListener.test.js +25 -11
  198. package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
  199. package/dist/src/ui/hooks/useKeypress.test.js +1 -1
  200. package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
  201. package/dist/src/ui/hooks/useLoadingIndicator.test.js +1 -1
  202. package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
  203. package/dist/src/ui/hooks/useMemoryMonitor.test.js +1 -1
  204. package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
  205. package/dist/src/ui/hooks/useMessageQueue.test.js +5 -4
  206. package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
  207. package/dist/src/ui/hooks/useModelCommand.test.js +7 -4
  208. package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
  209. package/dist/src/ui/hooks/usePhraseCycler.test.js +46 -16
  210. package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
  211. package/dist/src/ui/hooks/usePrivacySettings.test.js +11 -7
  212. package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
  213. package/dist/src/ui/hooks/useQuotaAndFallback.test.js +28 -14
  214. package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
  215. package/dist/src/ui/hooks/useSelectionList.test.js +116 -109
  216. package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
  217. package/dist/src/ui/hooks/useShellHistory.test.js +25 -17
  218. package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
  219. package/dist/src/ui/hooks/useSlashCompletion.js +18 -7
  220. package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
  221. package/dist/src/ui/hooks/useSlashCompletion.test.js +244 -111
  222. package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
  223. package/dist/src/ui/hooks/useTimer.test.js +1 -1
  224. package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
  225. package/dist/src/ui/hooks/useToolScheduler.test.js +6 -2
  226. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  227. package/dist/src/ui/hooks/vim.test.js +6 -21
  228. package/dist/src/ui/hooks/vim.test.js.map +1 -1
  229. package/dist/src/ui/state/extensions.d.ts +1 -0
  230. package/dist/src/ui/state/extensions.js +1 -0
  231. package/dist/src/ui/state/extensions.js.map +1 -1
  232. package/dist/src/ui/types.d.ts +1 -0
  233. package/dist/src/ui/types.js +2 -0
  234. package/dist/src/ui/types.js.map +1 -1
  235. package/dist/src/ui/utils/clipboardUtils.js +2 -2
  236. package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
  237. package/dist/src/ui/utils/updateCheck.js +6 -3
  238. package/dist/src/ui/utils/updateCheck.js.map +1 -1
  239. package/dist/src/ui/utils/updateCheck.test.js +5 -1
  240. package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
  241. package/dist/src/utils/commentJson.js +2 -2
  242. package/dist/src/utils/commentJson.js.map +1 -1
  243. package/dist/src/utils/commentJson.test.js +7 -6
  244. package/dist/src/utils/commentJson.test.js.map +1 -1
  245. package/dist/src/utils/version.js +6 -2
  246. package/dist/src/utils/version.js.map +1 -1
  247. package/dist/src/zed-integration/acp.js +2 -1
  248. package/dist/src/zed-integration/acp.js.map +1 -1
  249. package/dist/tsconfig.tsbuildinfo +1 -1
  250. package/package.json +3 -3
  251. package/dist/google-gemini-cli-0.12.0-preview.3.tgz +0 -0
  252. package/dist/src/utils/package.d.ts +0 -12
  253. package/dist/src/utils/package.js +0 -24
  254. package/dist/src/utils/package.js.map +0 -1
@@ -20,7 +20,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
20
20
  * - Display values for inherited and overridden settings
21
21
  *
22
22
  */
23
- import { render } from 'ink-testing-library';
23
+ import { render } from '../../test-utils/render.js';
24
+ import { waitFor } from '../../test-utils/async.js';
24
25
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
25
26
  import { SettingsDialog } from './SettingsDialog.js';
26
27
  import { LoadedSettings, SettingScope } from '../../config/settings.js';
@@ -196,9 +197,9 @@ const TOOLS_SHELL_FAKE_SCHEMA = {
196
197
  },
197
198
  },
198
199
  };
200
+ // Helper function to render SettingsDialog with standard wrapper
201
+ const renderDialog = (settings, onSelect, options) => render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect, onRestartRequest: options?.onRestartRequest, availableTerminalHeight: options?.availableTerminalHeight }) }));
199
202
  describe('SettingsDialog', () => {
200
- // Simple delay function for remaining tests that need gradual migration
201
- const wait = (ms = 50) => new Promise((resolve) => setTimeout(resolve, ms));
202
203
  beforeEach(() => {
203
204
  mockToggleVimEnabled.mockResolvedValue(true);
204
205
  });
@@ -211,83 +212,80 @@ describe('SettingsDialog', () => {
211
212
  it('should render the settings dialog with default state', () => {
212
213
  const settings = createMockSettings();
213
214
  const onSelect = vi.fn();
214
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
215
+ const { lastFrame } = renderDialog(settings, onSelect);
215
216
  const output = lastFrame();
216
217
  expect(output).toContain('Settings');
217
218
  expect(output).toContain('Apply To');
218
- expect(output).toContain('Use Enter to select, Tab to change focus, Esc to close');
219
+ // Use regex for more flexible help text matching
220
+ expect(output).toMatch(/Enter.*select.*Esc.*close/);
219
221
  });
220
222
  it('should accept availableTerminalHeight prop without errors', () => {
221
223
  const settings = createMockSettings();
222
224
  const onSelect = vi.fn();
223
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect, availableTerminalHeight: 20 }) }));
225
+ const { lastFrame } = renderDialog(settings, onSelect, {
226
+ availableTerminalHeight: 20,
227
+ });
224
228
  const output = lastFrame();
225
229
  // Should still render properly with the height prop
226
230
  expect(output).toContain('Settings');
227
- expect(output).toContain('Use Enter to select, Esc to close');
228
- });
229
- it('should show settings list with default values', () => {
230
- const settings = createMockSettings();
231
- const onSelect = vi.fn();
232
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
233
- const output = lastFrame();
234
- // Should show some default settings
235
- expect(output).toContain('●'); // Active indicator
231
+ // Use regex for more flexible help text matching
232
+ expect(output).toMatch(/Enter.*select.*Esc.*close/);
236
233
  });
237
- it('should highlight first setting by default', () => {
234
+ it('should render settings list with visual indicators', () => {
238
235
  const settings = createMockSettings();
239
236
  const onSelect = vi.fn();
240
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
237
+ const { lastFrame } = renderDialog(settings, onSelect);
241
238
  const output = lastFrame();
242
- // First item should be highlighted with green color and active indicator
243
- expect(output).toContain('●');
239
+ // Use snapshot to capture visual layout including indicators
240
+ expect(output).toMatchSnapshot();
244
241
  });
245
242
  });
246
243
  describe('Settings Navigation', () => {
247
- it('should navigate down with arrow key', async () => {
244
+ it.each([
245
+ {
246
+ name: 'arrow keys',
247
+ down: TerminalKeys.DOWN_ARROW,
248
+ up: TerminalKeys.UP_ARROW,
249
+ },
250
+ {
251
+ name: 'vim keys (j/k)',
252
+ down: 'j',
253
+ up: 'k',
254
+ },
255
+ ])('should navigate with $name', async ({ down, up }) => {
248
256
  const settings = createMockSettings();
249
257
  const onSelect = vi.fn();
250
- const { stdin, unmount, lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
251
- // Press down arrow
258
+ const { stdin, unmount, lastFrame } = renderDialog(settings, onSelect);
259
+ const initialFrame = lastFrame();
260
+ expect(initialFrame).toContain('Vim Mode');
261
+ // Navigate down
252
262
  act(() => {
253
- stdin.write(TerminalKeys.DOWN_ARROW); // Down arrow
263
+ stdin.write(down);
264
+ });
265
+ await waitFor(() => {
266
+ expect(lastFrame()).toContain('Disable Auto Update');
267
+ });
268
+ // Navigate up
269
+ act(() => {
270
+ stdin.write(up);
271
+ });
272
+ await waitFor(() => {
273
+ expect(lastFrame()).toContain('Vim Mode');
254
274
  });
255
- expect(lastFrame()).toContain('● Disable Auto Update');
256
- // The active index should have changed (tested indirectly through behavior)
257
- unmount();
258
- });
259
- it('should navigate up with arrow key', async () => {
260
- const settings = createMockSettings();
261
- const onSelect = vi.fn();
262
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
263
- // First go down, then up
264
- stdin.write(TerminalKeys.DOWN_ARROW); // Down arrow
265
- await wait();
266
- stdin.write(TerminalKeys.UP_ARROW);
267
- await wait();
268
- unmount();
269
- });
270
- it('should navigate with vim keys (j/k)', async () => {
271
- const settings = createMockSettings();
272
- const onSelect = vi.fn();
273
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
274
- // Navigate with vim keys
275
- stdin.write('j'); // Down
276
- await wait();
277
- stdin.write('k'); // Up
278
- await wait();
279
275
  unmount();
280
276
  });
281
277
  it('wraps around when at the top of the list', async () => {
282
278
  const settings = createMockSettings();
283
279
  const onSelect = vi.fn();
284
- const { stdin, unmount, lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
280
+ const { stdin, unmount, lastFrame } = renderDialog(settings, onSelect);
285
281
  // Try to go up from first item
286
282
  act(() => {
287
283
  stdin.write(TerminalKeys.UP_ARROW);
288
284
  });
289
- await wait();
290
- expect(lastFrame()).toContain('● Codebase Investigator Max Num Turns');
285
+ await waitFor(() => {
286
+ // Should wrap to last setting (without relying on exact bullet character)
287
+ expect(lastFrame()).toContain('Codebase Investigator Max Num Turns');
288
+ });
291
289
  unmount();
292
290
  });
293
291
  });
@@ -296,29 +294,28 @@ describe('SettingsDialog', () => {
296
294
  vi.mocked(saveModifiedSettings).mockClear();
297
295
  const settings = createMockSettings();
298
296
  const onSelect = vi.fn();
299
- const component = (_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
300
- const { stdin, unmount, lastFrame } = render(component);
297
+ const { stdin, unmount, lastFrame } = renderDialog(settings, onSelect);
301
298
  // Wait for initial render and verify we're on Vim Mode (first setting)
302
- await vi.waitFor(() => {
303
- expect(lastFrame()).toContain('Vim Mode');
299
+ await waitFor(() => {
300
+ expect(lastFrame()).toContain('Vim Mode');
304
301
  });
305
302
  // Navigate to Disable Auto Update setting and verify we're there
306
303
  act(() => {
307
304
  stdin.write(TerminalKeys.DOWN_ARROW);
308
305
  });
309
- await vi.waitFor(() => {
310
- expect(lastFrame()).toContain('Disable Auto Update');
306
+ await waitFor(() => {
307
+ expect(lastFrame()).toContain('Disable Auto Update');
311
308
  });
312
309
  // Toggle the setting
313
310
  act(() => {
314
311
  stdin.write(TerminalKeys.ENTER);
315
312
  });
316
313
  // Wait for the setting change to be processed
317
- await vi.waitFor(() => {
314
+ await waitFor(() => {
318
315
  expect(vi.mocked(saveModifiedSettings).mock.calls.length).toBeGreaterThan(0);
319
316
  });
320
317
  // Wait for the mock to be called
321
- await vi.waitFor(() => {
318
+ await waitFor(() => {
322
319
  expect(vi.mocked(saveModifiedSettings)).toHaveBeenCalled();
323
320
  });
324
321
  expect(vi.mocked(saveModifiedSettings)).toHaveBeenCalledWith(new Set(['general.disableAutoUpdate']), expect.objectContaining({
@@ -329,47 +326,36 @@ describe('SettingsDialog', () => {
329
326
  unmount();
330
327
  });
331
328
  describe('enum values', () => {
332
- it('toggles enum values with the enter key', async () => {
329
+ it.each([
330
+ {
331
+ name: 'toggles to next value',
332
+ initialValue: undefined,
333
+ expectedValue: StringEnum.BAZ,
334
+ },
335
+ {
336
+ name: 'loops back to first value when at end',
337
+ initialValue: StringEnum.BAZ,
338
+ expectedValue: StringEnum.FOO,
339
+ },
340
+ ])('$name', async ({ initialValue, expectedValue }) => {
333
341
  vi.mocked(saveModifiedSettings).mockClear();
334
342
  vi.mocked(getSettingsSchema).mockReturnValue(ENUM_FAKE_SCHEMA);
335
343
  const settings = createMockSettings();
344
+ if (initialValue !== undefined) {
345
+ settings.setValue(SettingScope.User, 'ui.theme', initialValue);
346
+ }
336
347
  const onSelect = vi.fn();
337
- const component = (_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
338
- const { stdin, unmount } = render(component);
339
- // Press Enter to toggle current setting
340
- stdin.write(TerminalKeys.DOWN_ARROW);
341
- await wait();
342
- stdin.write(TerminalKeys.ENTER);
343
- await wait();
344
- await vi.waitFor(() => {
345
- expect(vi.mocked(saveModifiedSettings)).toHaveBeenCalled();
348
+ const { stdin, unmount } = renderDialog(settings, onSelect);
349
+ act(() => {
350
+ stdin.write(TerminalKeys.DOWN_ARROW);
351
+ stdin.write(TerminalKeys.ENTER);
346
352
  });
347
- expect(vi.mocked(saveModifiedSettings)).toHaveBeenCalledWith(new Set(['ui.theme']), expect.objectContaining({
348
- ui: expect.objectContaining({
349
- theme: StringEnum.BAZ,
350
- }),
351
- }), expect.any(LoadedSettings), SettingScope.User);
352
- unmount();
353
- });
354
- it('loops back when reaching the end of an enum', async () => {
355
- vi.mocked(saveModifiedSettings).mockClear();
356
- vi.mocked(getSettingsSchema).mockReturnValue(ENUM_FAKE_SCHEMA);
357
- const settings = createMockSettings();
358
- settings.setValue(SettingScope.User, 'ui.theme', StringEnum.BAZ);
359
- const onSelect = vi.fn();
360
- const component = (_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
361
- const { stdin, unmount } = render(component);
362
- // Press Enter to toggle current setting
363
- stdin.write(TerminalKeys.DOWN_ARROW);
364
- await wait();
365
- stdin.write(TerminalKeys.ENTER);
366
- await wait();
367
- await vi.waitFor(() => {
353
+ await waitFor(() => {
368
354
  expect(vi.mocked(saveModifiedSettings)).toHaveBeenCalled();
369
355
  });
370
356
  expect(vi.mocked(saveModifiedSettings)).toHaveBeenCalledWith(new Set(['ui.theme']), expect.objectContaining({
371
357
  ui: expect.objectContaining({
372
- theme: StringEnum.FOO,
358
+ theme: expectedValue,
373
359
  }),
374
360
  }), expect.any(LoadedSettings), SettingScope.User);
375
361
  unmount();
@@ -378,20 +364,22 @@ describe('SettingsDialog', () => {
378
364
  it('should toggle setting with Space key', async () => {
379
365
  const settings = createMockSettings();
380
366
  const onSelect = vi.fn();
381
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
367
+ const { stdin, unmount } = renderDialog(settings, onSelect);
382
368
  // Press Space to toggle current setting
383
- stdin.write(' '); // Space key
384
- await wait();
369
+ act(() => {
370
+ stdin.write(' '); // Space key
371
+ });
385
372
  unmount();
386
373
  });
387
374
  it('should handle vim mode setting specially', async () => {
388
375
  const settings = createMockSettings();
389
376
  const onSelect = vi.fn();
390
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
377
+ const { stdin, unmount } = renderDialog(settings, onSelect);
391
378
  // Navigate to vim mode setting and toggle it
392
379
  // This would require knowing the exact position, so we'll just test that the mock is called
393
- stdin.write(TerminalKeys.ENTER); // Enter key
394
- await wait();
380
+ act(() => {
381
+ stdin.write(TerminalKeys.ENTER); // Enter key
382
+ });
395
383
  // The mock should potentially be called if vim mode was toggled
396
384
  unmount();
397
385
  });
@@ -400,26 +388,26 @@ describe('SettingsDialog', () => {
400
388
  it('should switch between scopes', async () => {
401
389
  const settings = createMockSettings();
402
390
  const onSelect = vi.fn();
403
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
391
+ const { stdin, unmount } = renderDialog(settings, onSelect);
404
392
  // Switch to scope focus
405
- stdin.write(TerminalKeys.TAB); // Tab key
406
- await wait();
407
- // Select different scope (numbers 1-3 typically available)
408
- stdin.write('2'); // Select second scope option
409
- await wait();
393
+ act(() => {
394
+ stdin.write(TerminalKeys.TAB); // Tab key
395
+ // Select different scope (numbers 1-3 typically available)
396
+ stdin.write('2'); // Select second scope option
397
+ });
410
398
  unmount();
411
399
  });
412
400
  it('should reset to settings focus when scope is selected', async () => {
413
401
  const settings = createMockSettings();
414
402
  const onSelect = vi.fn();
415
- const { lastFrame, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
403
+ const { lastFrame, unmount } = renderDialog(settings, onSelect);
416
404
  // Wait for initial render
417
- await vi.waitFor(() => {
405
+ await waitFor(() => {
418
406
  expect(lastFrame()).toContain('Vim Mode');
419
407
  });
420
408
  // The UI should show the settings section is active and scope section is inactive
421
- expect(lastFrame()).toContain('Vim Mode'); // Settings section active
422
- expect(lastFrame()).toContain(' Apply To'); // Scope section inactive
409
+ expect(lastFrame()).toContain('Vim Mode'); // Settings section active
410
+ expect(lastFrame()).toContain('Apply To'); // Scope section (don't rely on exact spacing)
423
411
  // This test validates the initial state - scope selection behavior
424
412
  // is complex due to keypress handling, so we focus on state validation
425
413
  unmount();
@@ -429,19 +417,23 @@ describe('SettingsDialog', () => {
429
417
  it('should show restart prompt for restart-required settings', async () => {
430
418
  const settings = createMockSettings();
431
419
  const onRestartRequest = vi.fn();
432
- const { unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: () => { }, onRestartRequest: onRestartRequest }) }));
420
+ const { unmount } = renderDialog(settings, vi.fn(), {
421
+ onRestartRequest,
422
+ });
433
423
  // This test would need to trigger a restart-required setting change
434
424
  // The exact steps depend on which settings require restart
435
- await wait();
436
425
  unmount();
437
426
  });
438
427
  it('should handle restart request when r is pressed', async () => {
439
428
  const settings = createMockSettings();
440
429
  const onRestartRequest = vi.fn();
441
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: () => { }, onRestartRequest: onRestartRequest }) }));
430
+ const { stdin, unmount } = renderDialog(settings, vi.fn(), {
431
+ onRestartRequest,
432
+ });
442
433
  // Press 'r' key (this would only work if restart prompt is showing)
443
- stdin.write('r');
444
- await wait();
434
+ act(() => {
435
+ stdin.write('r');
436
+ });
445
437
  // If restart prompt was showing, onRestartRequest should be called
446
438
  unmount();
447
439
  });
@@ -450,9 +442,9 @@ describe('SettingsDialog', () => {
450
442
  it('should call onSelect with undefined when Escape is pressed', async () => {
451
443
  const settings = createMockSettings();
452
444
  const onSelect = vi.fn();
453
- const { lastFrame, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
445
+ const { lastFrame, unmount } = renderDialog(settings, onSelect);
454
446
  // Wait for initial render
455
- await vi.waitFor(() => {
447
+ await waitFor(() => {
456
448
  expect(lastFrame()).toContain('Hide Window Title');
457
449
  });
458
450
  // Verify the dialog is rendered properly
@@ -467,13 +459,12 @@ describe('SettingsDialog', () => {
467
459
  it('should persist settings across scope changes', async () => {
468
460
  const settings = createMockSettings({ vimMode: true });
469
461
  const onSelect = vi.fn();
470
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
471
- // Switch to scope selector
472
- stdin.write(TerminalKeys.TAB); // Tab
473
- await wait();
474
- // Change scope
475
- stdin.write('2'); // Select workspace scope
476
- await wait();
462
+ const { stdin, unmount } = renderDialog(settings, onSelect);
463
+ // Switch to scope selector and change scope
464
+ act(() => {
465
+ stdin.write(TerminalKeys.TAB); // Tab
466
+ stdin.write('2'); // Select workspace scope
467
+ });
477
468
  // Settings should be reloaded for new scope
478
469
  unmount();
479
470
  });
@@ -482,7 +473,7 @@ describe('SettingsDialog', () => {
482
473
  { vimMode: false }, // System settings
483
474
  { autoUpdate: false });
484
475
  const onSelect = vi.fn();
485
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
476
+ const { lastFrame } = renderDialog(settings, onSelect);
486
477
  // Should show user scope values initially
487
478
  const output = lastFrame();
488
479
  expect(output).toContain('Settings');
@@ -493,10 +484,11 @@ describe('SettingsDialog', () => {
493
484
  mockToggleVimEnabled.mockRejectedValue(new Error('Toggle failed'));
494
485
  const settings = createMockSettings();
495
486
  const onSelect = vi.fn();
496
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
487
+ const { stdin, unmount } = renderDialog(settings, onSelect);
497
488
  // Try to toggle a setting (this might trigger vim mode toggle)
498
- stdin.write(TerminalKeys.ENTER); // Enter
499
- await wait();
489
+ act(() => {
490
+ stdin.write(TerminalKeys.ENTER); // Enter
491
+ });
500
492
  // Should not crash
501
493
  unmount();
502
494
  });
@@ -505,27 +497,26 @@ describe('SettingsDialog', () => {
505
497
  it('should track modified settings correctly', async () => {
506
498
  const settings = createMockSettings();
507
499
  const onSelect = vi.fn();
508
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
509
- // Toggle a setting
510
- stdin.write(TerminalKeys.ENTER); // Enter
511
- await wait();
512
- // Toggle another setting
513
- stdin.write(TerminalKeys.DOWN_ARROW); // Down
514
- await wait();
515
- stdin.write(TerminalKeys.ENTER); // Enter
516
- await wait();
500
+ const { stdin, unmount } = renderDialog(settings, onSelect);
501
+ // Toggle a setting, then toggle another setting
502
+ act(() => {
503
+ stdin.write(TerminalKeys.ENTER); // Enter
504
+ stdin.write(TerminalKeys.DOWN_ARROW); // Down
505
+ stdin.write(TerminalKeys.ENTER); // Enter
506
+ });
517
507
  // Should track multiple modified settings
518
508
  unmount();
519
509
  });
520
510
  it('should handle scrolling when there are many settings', async () => {
521
511
  const settings = createMockSettings();
522
512
  const onSelect = vi.fn();
523
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
513
+ const { stdin, unmount } = renderDialog(settings, onSelect);
524
514
  // Navigate down many times to test scrolling
525
- for (let i = 0; i < 10; i++) {
526
- stdin.write(TerminalKeys.DOWN_ARROW); // Down arrow
527
- await wait(10);
528
- }
515
+ act(() => {
516
+ for (let i = 0; i < 10; i++) {
517
+ stdin.write(TerminalKeys.DOWN_ARROW); // Down arrow
518
+ }
519
+ });
529
520
  unmount();
530
521
  });
531
522
  });
@@ -536,8 +527,9 @@ describe('SettingsDialog', () => {
536
527
  const { stdin, unmount } = render(_jsx(VimModeProvider, { settings: settings, children: _jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }) }));
537
528
  // Navigate to and toggle vim mode setting
538
529
  // This would require knowing the exact position of vim mode setting
539
- stdin.write(TerminalKeys.ENTER); // Enter
540
- await wait();
530
+ act(() => {
531
+ stdin.write(TerminalKeys.ENTER); // Enter
532
+ });
541
533
  unmount();
542
534
  });
543
535
  });
@@ -547,7 +539,7 @@ describe('SettingsDialog', () => {
547
539
  { hideWindowTitle: true }, // System settings
548
540
  { ideMode: false });
549
541
  const onSelect = vi.fn();
550
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
542
+ const { lastFrame } = renderDialog(settings, onSelect);
551
543
  const output = lastFrame();
552
544
  // Should contain settings labels
553
545
  expect(output).toContain('Settings');
@@ -555,28 +547,30 @@ describe('SettingsDialog', () => {
555
547
  it('should handle immediate settings save for non-restart-required settings', async () => {
556
548
  const settings = createMockSettings();
557
549
  const onSelect = vi.fn();
558
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
550
+ const { stdin, unmount } = renderDialog(settings, onSelect);
559
551
  // Toggle a non-restart-required setting (like hideTips)
560
- stdin.write(TerminalKeys.ENTER); // Enter - toggle current setting
561
- await wait();
552
+ act(() => {
553
+ stdin.write(TerminalKeys.ENTER); // Enter - toggle current setting
554
+ });
562
555
  // Should save immediately without showing restart prompt
563
556
  unmount();
564
557
  });
565
558
  it('should show restart prompt for restart-required settings', async () => {
566
559
  const settings = createMockSettings();
567
560
  const onSelect = vi.fn();
568
- const { lastFrame, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
561
+ const { lastFrame, unmount } = renderDialog(settings, onSelect);
569
562
  // This test would need to navigate to a specific restart-required setting
570
563
  // Since we can't easily target specific settings, we test the general behavior
571
- await wait();
572
564
  // Should not show restart prompt initially
573
- expect(lastFrame()).not.toContain('To see changes, Gemini CLI must be restarted');
565
+ await waitFor(() => {
566
+ expect(lastFrame()).not.toContain('To see changes, Gemini CLI must be restarted');
567
+ });
574
568
  unmount();
575
569
  });
576
570
  it('should clear restart prompt when switching scopes', async () => {
577
571
  const settings = createMockSettings();
578
572
  const onSelect = vi.fn();
579
- const { unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
573
+ const { unmount } = renderDialog(settings, onSelect);
580
574
  // Restart prompt should be cleared when switching scopes
581
575
  unmount();
582
576
  });
@@ -586,7 +580,7 @@ describe('SettingsDialog', () => {
586
580
  const settings = createMockSettings({}, { vimMode: true, hideWindowTitle: false }, // System settings
587
581
  {});
588
582
  const onSelect = vi.fn();
589
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
583
+ const { lastFrame } = renderDialog(settings, onSelect);
590
584
  const output = lastFrame();
591
585
  // Settings should show inherited values
592
586
  expect(output).toContain('Settings');
@@ -596,89 +590,69 @@ describe('SettingsDialog', () => {
596
590
  { vimMode: true }, // System default
597
591
  {});
598
592
  const onSelect = vi.fn();
599
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
593
+ const { lastFrame } = renderDialog(settings, onSelect);
600
594
  const output = lastFrame();
601
595
  // Should show settings with override indicators
602
596
  expect(output).toContain('Settings');
603
597
  });
604
598
  });
605
599
  describe('Race Condition Regression Tests', () => {
606
- it('should not reset sibling settings when toggling a nested setting multiple times', async () => {
607
- vi.mocked(saveModifiedSettings).mockClear();
608
- vi.mocked(getSettingsSchema).mockReturnValue(TOOLS_SHELL_FAKE_SCHEMA);
609
- const settings = createMockSettings({
610
- tools: {
611
- shell: {
612
- showColor: false,
613
- enableInteractiveShell: true,
614
- },
600
+ it.each([
601
+ {
602
+ name: 'not reset sibling settings when toggling a nested setting multiple times',
603
+ toggleCount: 5,
604
+ shellSettings: {
605
+ showColor: false,
606
+ enableInteractiveShell: true,
615
607
  },
616
- });
617
- const onSelect = vi.fn();
618
- const component = (_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
619
- const { stdin, unmount } = render(component);
620
- await wait();
621
- // Toggle showColor 5 times to trigger race condition
622
- for (let i = 0; i < 5; i++) {
623
- act(() => {
624
- stdin.write(TerminalKeys.ENTER);
625
- });
626
- await wait(50);
627
- }
628
- await vi.waitFor(() => {
629
- expect(vi.mocked(saveModifiedSettings).mock.calls.length).toBeGreaterThan(0);
630
- });
631
- // Verify sibling settings are preserved
632
- const calls = vi.mocked(saveModifiedSettings).mock.calls;
633
- calls.forEach((call) => {
634
- const [modifiedKeys, pendingSettings] = call;
635
- if (modifiedKeys.has('tools.shell.showColor')) {
636
- expect(pendingSettings.tools?.shell?.enableInteractiveShell).toBe(true);
637
- expect(modifiedKeys.has('tools.shell.enableInteractiveShell')).toBe(false);
638
- }
639
- });
640
- expect(calls.length).toBeGreaterThan(0);
641
- unmount();
642
- });
643
- it('should preserve multiple sibling settings in nested objects during rapid toggles', async () => {
608
+ expectedSiblings: {
609
+ enableInteractiveShell: true,
610
+ },
611
+ },
612
+ {
613
+ name: 'preserve multiple sibling settings in nested objects during rapid toggles',
614
+ toggleCount: 3,
615
+ shellSettings: {
616
+ showColor: false,
617
+ enableInteractiveShell: true,
618
+ pager: 'less',
619
+ },
620
+ expectedSiblings: {
621
+ enableInteractiveShell: true,
622
+ pager: 'less',
623
+ },
624
+ },
625
+ ])('should $name', async ({ toggleCount, shellSettings, expectedSiblings }) => {
644
626
  vi.mocked(saveModifiedSettings).mockClear();
645
627
  vi.mocked(getSettingsSchema).mockReturnValue(TOOLS_SHELL_FAKE_SCHEMA);
646
628
  const settings = createMockSettings({
647
629
  tools: {
648
- shell: {
649
- showColor: false,
650
- enableInteractiveShell: true,
651
- pager: 'less',
652
- },
630
+ shell: shellSettings,
653
631
  },
654
632
  });
655
633
  const onSelect = vi.fn();
656
- const component = (_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
657
- const { stdin, unmount } = render(component);
658
- await wait();
659
- // Rapid toggles
660
- for (let i = 0; i < 3; i++) {
634
+ const { stdin, unmount } = renderDialog(settings, onSelect);
635
+ for (let i = 0; i < toggleCount; i++) {
661
636
  act(() => {
662
637
  stdin.write(TerminalKeys.ENTER);
663
638
  });
664
- await wait(30);
665
639
  }
666
- await vi.waitFor(() => {
640
+ await waitFor(() => {
667
641
  expect(vi.mocked(saveModifiedSettings).mock.calls.length).toBeGreaterThan(0);
668
642
  });
669
- // Verify all siblings preserved
670
643
  const calls = vi.mocked(saveModifiedSettings).mock.calls;
671
644
  calls.forEach((call) => {
672
645
  const [modifiedKeys, pendingSettings] = call;
673
646
  if (modifiedKeys.has('tools.shell.showColor')) {
674
647
  const shellSettings = pendingSettings.tools?.shell;
675
- expect(shellSettings?.['enableInteractiveShell']).toBe(true);
676
- expect(shellSettings?.['pager']).toBe('less');
648
+ Object.entries(expectedSiblings).forEach(([key, value]) => {
649
+ expect(shellSettings?.[key]).toBe(value);
650
+ expect(modifiedKeys.has(`tools.shell.${key}`)).toBe(false);
651
+ });
677
652
  expect(modifiedKeys.size).toBe(1);
678
- expect(modifiedKeys.has('tools.shell.enableInteractiveShell')).toBe(false);
679
- expect(modifiedKeys.has('tools.shell.pager')).toBe(false);
680
653
  }
681
654
  });
655
+ expect(calls.length).toBeGreaterThan(0);
682
656
  unmount();
683
657
  });
684
658
  });
@@ -686,58 +660,52 @@ describe('SettingsDialog', () => {
686
660
  it('should handle rapid key presses gracefully', async () => {
687
661
  const settings = createMockSettings();
688
662
  const onSelect = vi.fn();
689
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
663
+ const { stdin, unmount } = renderDialog(settings, onSelect);
690
664
  // Rapid navigation
691
- for (let i = 0; i < 5; i++) {
692
- stdin.write(TerminalKeys.DOWN_ARROW);
693
- stdin.write(TerminalKeys.UP_ARROW);
694
- }
695
- await wait(100);
665
+ act(() => {
666
+ for (let i = 0; i < 5; i++) {
667
+ stdin.write(TerminalKeys.DOWN_ARROW);
668
+ stdin.write(TerminalKeys.UP_ARROW);
669
+ }
670
+ });
696
671
  // Should not crash
697
672
  unmount();
698
673
  });
699
- it('should handle Ctrl+C to reset current setting to default', async () => {
700
- const settings = createMockSettings({ vimMode: true }); // Start with vimMode enabled
701
- const onSelect = vi.fn();
702
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
703
- // Press Ctrl+C to reset current setting to default
704
- stdin.write('\u0003'); // Ctrl+C
705
- await wait();
706
- // Should reset the current setting to its default value
707
- unmount();
708
- });
709
- it('should handle Ctrl+L to reset current setting to default', async () => {
710
- const settings = createMockSettings({ vimMode: true }); // Start with vimMode enabled
674
+ it.each([
675
+ { key: 'Ctrl+C', code: '\u0003' },
676
+ { key: 'Ctrl+L', code: '\u000C' },
677
+ ])('should handle $key to reset current setting to default', async ({ code }) => {
678
+ const settings = createMockSettings({ vimMode: true });
711
679
  const onSelect = vi.fn();
712
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
713
- // Press Ctrl+L to reset current setting to default
714
- stdin.write('\u000C'); // Ctrl+L
715
- await wait();
680
+ const { stdin, unmount } = renderDialog(settings, onSelect);
681
+ act(() => {
682
+ stdin.write(code);
683
+ });
716
684
  // Should reset the current setting to its default value
717
685
  unmount();
718
686
  });
719
687
  it('should handle navigation when only one setting exists', async () => {
720
688
  const settings = createMockSettings();
721
689
  const onSelect = vi.fn();
722
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
690
+ const { stdin, unmount } = renderDialog(settings, onSelect);
723
691
  // Try to navigate when potentially at bounds
724
- stdin.write(TerminalKeys.DOWN_ARROW);
725
- await wait();
726
- stdin.write(TerminalKeys.UP_ARROW);
727
- await wait();
692
+ act(() => {
693
+ stdin.write(TerminalKeys.DOWN_ARROW);
694
+ stdin.write(TerminalKeys.UP_ARROW);
695
+ });
728
696
  unmount();
729
697
  });
730
698
  it('should properly handle Tab navigation between sections', async () => {
731
699
  const settings = createMockSettings();
732
700
  const onSelect = vi.fn();
733
- const { lastFrame, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
701
+ const { lastFrame, unmount } = renderDialog(settings, onSelect);
734
702
  // Wait for initial render
735
- await vi.waitFor(() => {
703
+ await waitFor(() => {
736
704
  expect(lastFrame()).toContain('Vim Mode');
737
705
  });
738
706
  // Verify initial state: settings section active, scope section inactive
739
- expect(lastFrame()).toContain('Vim Mode'); // Settings section active
740
- expect(lastFrame()).toContain(' Apply To'); // Scope section inactive
707
+ expect(lastFrame()).toContain('Vim Mode'); // Settings section active
708
+ expect(lastFrame()).toContain('Apply To'); // Scope section (don't rely on exact spacing)
741
709
  // This test validates the rendered UI structure for tab navigation
742
710
  // Actual tab behavior testing is complex due to keypress handling
743
711
  unmount();
@@ -749,7 +717,7 @@ describe('SettingsDialog', () => {
749
717
  const settings = createMockSettings({ vimMode: null }, // Invalid value
750
718
  {}, {});
751
719
  const onSelect = vi.fn();
752
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
720
+ const { lastFrame } = renderDialog(settings, onSelect);
753
721
  // Should still render without crashing
754
722
  expect(lastFrame()).toContain('Settings');
755
723
  });
@@ -757,7 +725,7 @@ describe('SettingsDialog', () => {
757
725
  const settings = createMockSettings();
758
726
  const onSelect = vi.fn();
759
727
  // Should not crash even if some settings are missing definitions
760
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
728
+ const { lastFrame } = renderDialog(settings, onSelect);
761
729
  expect(lastFrame()).toContain('Settings');
762
730
  });
763
731
  });
@@ -765,17 +733,18 @@ describe('SettingsDialog', () => {
765
733
  it('should handle complete user workflow: navigate, toggle, change scope, exit', async () => {
766
734
  const settings = createMockSettings();
767
735
  const onSelect = vi.fn();
768
- const { lastFrame, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
736
+ const { lastFrame, unmount } = renderDialog(settings, onSelect);
769
737
  // Wait for initial render
770
- await vi.waitFor(() => {
738
+ await waitFor(() => {
771
739
  expect(lastFrame()).toContain('Vim Mode');
772
740
  });
773
741
  // Verify the complete UI is rendered with all necessary sections
774
742
  expect(lastFrame()).toContain('Settings'); // Title
775
- expect(lastFrame()).toContain('Vim Mode'); // Active setting
743
+ expect(lastFrame()).toContain('Vim Mode'); // Active setting
776
744
  expect(lastFrame()).toContain('Apply To'); // Scope section
777
745
  expect(lastFrame()).toContain('User Settings'); // Scope options (no numbers when settings focused)
778
- expect(lastFrame()).toContain('(Use Enter to select, Tab to change focus, Esc to close)'); // Help text
746
+ // Use regex for more flexible help text matching
747
+ expect(lastFrame()).toMatch(/Enter.*select.*Tab.*focus.*Esc.*close/);
779
748
  // This test validates the complete UI structure is available for user workflow
780
749
  // Individual interactions are tested in focused unit tests
781
750
  unmount();
@@ -783,20 +752,15 @@ describe('SettingsDialog', () => {
783
752
  it('should allow changing multiple settings without losing pending changes', async () => {
784
753
  const settings = createMockSettings();
785
754
  const onSelect = vi.fn();
786
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
787
- // Toggle first setting (should require restart)
788
- stdin.write(TerminalKeys.ENTER); // Enter
789
- await wait();
790
- // Navigate to next setting and toggle it (should not require restart - e.g., vimMode)
791
- stdin.write(TerminalKeys.DOWN_ARROW); // Down
792
- await wait();
793
- stdin.write(TerminalKeys.ENTER); // Enter
794
- await wait();
795
- // Navigate to another setting and toggle it (should also require restart)
796
- stdin.write(TerminalKeys.DOWN_ARROW); // Down
797
- await wait();
798
- stdin.write(TerminalKeys.ENTER); // Enter
799
- await wait();
755
+ const { stdin, unmount } = renderDialog(settings, onSelect);
756
+ // Toggle multiple settings
757
+ act(() => {
758
+ stdin.write(TerminalKeys.ENTER); // Enter
759
+ stdin.write(TerminalKeys.DOWN_ARROW); // Down
760
+ stdin.write(TerminalKeys.ENTER); // Enter
761
+ stdin.write(TerminalKeys.DOWN_ARROW); // Down
762
+ stdin.write(TerminalKeys.ENTER); // Enter
763
+ });
800
764
  // The test verifies that all changes are preserved and the dialog still works
801
765
  // This tests the fix for the bug where changing one setting would reset all pending changes
802
766
  unmount();
@@ -804,28 +768,28 @@ describe('SettingsDialog', () => {
804
768
  it('should maintain state consistency during complex interactions', async () => {
805
769
  const settings = createMockSettings({ vimMode: true });
806
770
  const onSelect = vi.fn();
807
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
771
+ const { stdin, unmount } = renderDialog(settings, onSelect);
808
772
  // Multiple scope changes
809
- stdin.write(TerminalKeys.TAB); // Tab to scope
810
- await wait();
811
- stdin.write('2'); // Workspace
812
- await wait();
813
- stdin.write(TerminalKeys.TAB); // Tab to settings
814
- await wait();
815
- stdin.write(TerminalKeys.TAB); // Tab to scope
816
- await wait();
817
- stdin.write('1'); // User
818
- await wait();
773
+ act(() => {
774
+ stdin.write(TerminalKeys.TAB); // Tab to scope
775
+ stdin.write('2'); // Workspace
776
+ stdin.write(TerminalKeys.TAB); // Tab to settings
777
+ stdin.write(TerminalKeys.TAB); // Tab to scope
778
+ stdin.write('1'); // User
779
+ });
819
780
  // Should maintain consistent state
820
781
  unmount();
821
782
  });
822
783
  it('should handle restart workflow correctly', async () => {
823
784
  const settings = createMockSettings();
824
785
  const onRestartRequest = vi.fn();
825
- const { stdin, unmount } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: () => { }, onRestartRequest: onRestartRequest }) }));
786
+ const { stdin, unmount } = renderDialog(settings, vi.fn(), {
787
+ onRestartRequest,
788
+ });
826
789
  // This would test the restart workflow if we could trigger it
827
- stdin.write('r'); // Try restart key
828
- await wait();
790
+ act(() => {
791
+ stdin.write('r'); // Try restart key
792
+ });
829
793
  // Without restart prompt showing, this should have no effect
830
794
  expect(onRestartRequest).not.toHaveBeenCalled();
831
795
  unmount();
@@ -836,282 +800,244 @@ describe('SettingsDialog', () => {
836
800
  let settings = createMockSettings({ 'a.string.setting': 'initial' });
837
801
  const onSelect = vi.fn();
838
802
  const { stdin, unmount, rerender } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
839
- // Wait for the dialog to render
840
- await wait();
841
803
  // Navigate to the last setting
842
- for (let i = 0; i < 20; i++) {
843
- stdin.write('j'); // Down
844
- await wait(10);
845
- }
846
- // Press Enter to start editing
847
- stdin.write('\r');
848
- await wait();
849
- // Type a new value
850
- stdin.write('new value');
851
- await wait();
852
- // Press Enter to commit
853
- stdin.write('\r');
854
- await wait();
804
+ act(() => {
805
+ for (let i = 0; i < 20; i++) {
806
+ stdin.write('j'); // Down
807
+ }
808
+ });
809
+ // Press Enter to start editing, type new value, and commit
810
+ act(() => {
811
+ stdin.write('\r'); // Start editing
812
+ stdin.write('new value');
813
+ stdin.write('\r'); // Commit
814
+ });
855
815
  settings = createMockSettings({ 'a.string.setting': 'new value' }, {}, {});
856
816
  rerender(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
857
- await wait();
858
817
  // Press Escape to exit
859
- stdin.write('\u001B');
860
- await wait(60);
861
- expect(onSelect).toHaveBeenCalledWith(undefined, 'User');
818
+ act(() => {
819
+ stdin.write('\u001B');
820
+ });
821
+ await waitFor(() => {
822
+ expect(onSelect).toHaveBeenCalledWith(undefined, 'User');
823
+ });
862
824
  unmount();
863
825
  });
864
826
  });
865
827
  describe('Snapshot Tests', () => {
866
828
  /**
867
829
  * Snapshot tests for SettingsDialog component using ink-testing-library.
868
- * These tests capture the visual output of the component in various states:
869
- *
870
- * - Default rendering with no custom settings
871
- * - Various combinations of boolean settings (enabled/disabled)
872
- * - Mixed boolean and number settings configurations
873
- * - Different focus states (settings vs scope selector)
874
- * - Different scope selections (User, System, Workspace)
875
- * - Accessibility settings enabled
876
- * - File filtering configurations
877
- * - Tools and security settings
878
- * - All settings disabled state
879
- *
830
+ * These tests capture the visual output of the component in various states.
880
831
  * The snapshots help ensure UI consistency and catch unintended visual changes.
881
832
  */
882
- it('should render default state correctly', () => {
883
- const settings = createMockSettings();
884
- const onSelect = vi.fn();
885
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
886
- expect(lastFrame()).toMatchSnapshot();
887
- });
888
- it('should render with various boolean settings enabled', () => {
889
- const settings = createMockSettings({
890
- general: {
891
- vimMode: true,
892
- disableAutoUpdate: true,
893
- debugKeystrokeLogging: true,
894
- enablePromptCompletion: true,
895
- },
896
- ui: {
897
- hideWindowTitle: true,
898
- hideTips: true,
899
- showMemoryUsage: true,
900
- showLineNumbers: true,
901
- showCitations: true,
902
- accessibility: {
903
- disableLoadingPhrases: true,
904
- screenReader: true,
833
+ it.each([
834
+ {
835
+ name: 'default state',
836
+ userSettings: {},
837
+ systemSettings: {},
838
+ workspaceSettings: {},
839
+ stdinActions: undefined,
840
+ },
841
+ {
842
+ name: 'various boolean settings enabled',
843
+ userSettings: {
844
+ general: {
845
+ vimMode: true,
846
+ disableAutoUpdate: true,
847
+ debugKeystrokeLogging: true,
848
+ enablePromptCompletion: true,
905
849
  },
906
- },
907
- ide: {
908
- enabled: true,
909
- },
910
- context: {
911
- loadMemoryFromIncludeDirectories: true,
912
- fileFiltering: {
913
- respectGitIgnore: true,
914
- respectGeminiIgnore: true,
915
- enableRecursiveFileSearch: true,
916
- disableFuzzySearch: false,
850
+ ui: {
851
+ hideWindowTitle: true,
852
+ hideTips: true,
853
+ showMemoryUsage: true,
854
+ showLineNumbers: true,
855
+ showCitations: true,
856
+ accessibility: {
857
+ disableLoadingPhrases: true,
858
+ screenReader: true,
859
+ },
917
860
  },
918
- },
919
- tools: {
920
- enableInteractiveShell: true,
921
- autoAccept: true,
922
- useRipgrep: true,
923
- },
924
- security: {
925
- folderTrust: {
861
+ ide: {
926
862
  enabled: true,
927
863
  },
864
+ context: {
865
+ loadMemoryFromIncludeDirectories: true,
866
+ fileFiltering: {
867
+ respectGitIgnore: true,
868
+ respectGeminiIgnore: true,
869
+ enableRecursiveFileSearch: true,
870
+ disableFuzzySearch: false,
871
+ },
872
+ },
873
+ tools: {
874
+ enableInteractiveShell: true,
875
+ autoAccept: true,
876
+ useRipgrep: true,
877
+ },
878
+ security: {
879
+ folderTrust: {
880
+ enabled: true,
881
+ },
882
+ },
928
883
  },
929
- });
930
- const onSelect = vi.fn();
931
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
932
- expect(lastFrame()).toMatchSnapshot();
933
- });
934
- it('should render with mixed boolean and number settings', () => {
935
- const settings = createMockSettings({
936
- general: {
937
- vimMode: false,
938
- disableAutoUpdate: true,
939
- },
940
- ui: {
941
- showMemoryUsage: true,
942
- hideWindowTitle: false,
943
- },
944
- tools: {
945
- truncateToolOutputThreshold: 50000,
946
- truncateToolOutputLines: 1000,
947
- },
948
- context: {
949
- discoveryMaxDirs: 500,
950
- },
951
- model: {
952
- maxSessionTurns: 100,
953
- skipNextSpeakerCheck: false,
954
- },
955
- });
956
- const onSelect = vi.fn();
957
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
958
- expect(lastFrame()).toMatchSnapshot();
959
- });
960
- it('should render focused on scope selector', () => {
961
- const settings = createMockSettings();
962
- const onSelect = vi.fn();
963
- const { lastFrame, stdin } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
964
- // Switch focus to scope selector with Tab
965
- stdin.write('\t');
966
- expect(lastFrame()).toMatchSnapshot();
967
- });
968
- it('should render with different scope selected (System)', () => {
969
- const settings = createMockSettings({}, // userSettings
884
+ systemSettings: {},
885
+ workspaceSettings: {},
886
+ stdinActions: undefined,
887
+ },
970
888
  {
971
- // systemSettings
972
- general: {
973
- vimMode: true,
974
- disableAutoUpdate: false,
975
- },
976
- ui: {
977
- showMemoryUsage: true,
889
+ name: 'mixed boolean and number settings',
890
+ userSettings: {
891
+ general: {
892
+ vimMode: false,
893
+ disableAutoUpdate: true,
894
+ },
895
+ ui: {
896
+ showMemoryUsage: true,
897
+ hideWindowTitle: false,
898
+ },
899
+ tools: {
900
+ truncateToolOutputThreshold: 50000,
901
+ truncateToolOutputLines: 1000,
902
+ },
903
+ context: {
904
+ discoveryMaxDirs: 500,
905
+ },
906
+ model: {
907
+ maxSessionTurns: 100,
908
+ skipNextSpeakerCheck: false,
909
+ },
978
910
  },
979
- });
980
- const onSelect = vi.fn();
981
- const { lastFrame, stdin } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
982
- // Switch to scope selector
983
- stdin.write('\t');
984
- // Navigate to System scope
985
- stdin.write('ArrowDown');
986
- stdin.write('\r'); // Enter to select
987
- expect(lastFrame()).toMatchSnapshot();
988
- });
989
- it('should render with different scope selected (Workspace)', () => {
990
- const settings = createMockSettings({}, // userSettings
991
- {}, // systemSettings
911
+ systemSettings: {},
912
+ workspaceSettings: {},
913
+ stdinActions: undefined,
914
+ },
992
915
  {
993
- // workspaceSettings
994
- general: {
995
- vimMode: false,
996
- debugKeystrokeLogging: true,
916
+ name: 'focused on scope selector',
917
+ userSettings: {},
918
+ systemSettings: {},
919
+ workspaceSettings: {},
920
+ stdinActions: (stdin) => {
921
+ act(() => {
922
+ stdin.write('\t');
923
+ });
997
924
  },
998
- tools: {
999
- useRipgrep: true,
1000
- enableInteractiveShell: false,
1001
- },
1002
- });
1003
- const onSelect = vi.fn();
1004
- const { lastFrame, stdin } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
1005
- // Switch to scope selector
1006
- stdin.write('\t');
1007
- // Navigate to Workspace scope (down twice)
1008
- stdin.write('ArrowDown');
1009
- stdin.write('ArrowDown');
1010
- stdin.write('\r'); // Enter to select
1011
- expect(lastFrame()).toMatchSnapshot();
1012
- });
1013
- it('should render with accessibility settings enabled', () => {
1014
- const settings = createMockSettings({
1015
- ui: {
1016
- accessibility: {
1017
- disableLoadingPhrases: true,
1018
- screenReader: true,
925
+ },
926
+ {
927
+ name: 'accessibility settings enabled',
928
+ userSettings: {
929
+ ui: {
930
+ accessibility: {
931
+ disableLoadingPhrases: true,
932
+ screenReader: true,
933
+ },
934
+ showMemoryUsage: true,
935
+ showLineNumbers: true,
1019
936
  },
1020
- showMemoryUsage: true,
1021
- showLineNumbers: true,
1022
- },
1023
- general: {
1024
- vimMode: true,
1025
- },
1026
- });
1027
- const onSelect = vi.fn();
1028
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
1029
- expect(lastFrame()).toMatchSnapshot();
1030
- });
1031
- it('should render with file filtering settings configured', () => {
1032
- const settings = createMockSettings({
1033
- context: {
1034
- fileFiltering: {
1035
- respectGitIgnore: false,
1036
- respectGeminiIgnore: true,
1037
- enableRecursiveFileSearch: false,
1038
- disableFuzzySearch: true,
937
+ general: {
938
+ vimMode: true,
1039
939
  },
1040
- loadMemoryFromIncludeDirectories: true,
1041
- discoveryMaxDirs: 100,
1042
- },
1043
- });
1044
- const onSelect = vi.fn();
1045
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
1046
- expect(lastFrame()).toMatchSnapshot();
1047
- });
1048
- it('should render with tools and security settings', () => {
1049
- const settings = createMockSettings({
1050
- tools: {
1051
- enableInteractiveShell: true,
1052
- autoAccept: false,
1053
- useRipgrep: true,
1054
- truncateToolOutputThreshold: 25000,
1055
- truncateToolOutputLines: 500,
1056
940
  },
1057
- security: {
1058
- folderTrust: {
1059
- enabled: true,
941
+ systemSettings: {},
942
+ workspaceSettings: {},
943
+ stdinActions: undefined,
944
+ },
945
+ {
946
+ name: 'file filtering settings configured',
947
+ userSettings: {
948
+ context: {
949
+ fileFiltering: {
950
+ respectGitIgnore: false,
951
+ respectGeminiIgnore: true,
952
+ enableRecursiveFileSearch: false,
953
+ disableFuzzySearch: true,
954
+ },
955
+ loadMemoryFromIncludeDirectories: true,
956
+ discoveryMaxDirs: 100,
1060
957
  },
1061
958
  },
1062
- model: {
1063
- maxSessionTurns: 50,
1064
- skipNextSpeakerCheck: true,
1065
- },
1066
- });
1067
- const onSelect = vi.fn();
1068
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
1069
- expect(lastFrame()).toMatchSnapshot();
1070
- });
1071
- it('should render with all boolean settings disabled', () => {
1072
- const settings = createMockSettings({
1073
- general: {
1074
- vimMode: false,
1075
- disableAutoUpdate: false,
1076
- debugKeystrokeLogging: false,
1077
- enablePromptCompletion: false,
1078
- },
1079
- ui: {
1080
- hideWindowTitle: false,
1081
- hideTips: false,
1082
- showMemoryUsage: false,
1083
- showLineNumbers: false,
1084
- showCitations: false,
1085
- accessibility: {
1086
- disableLoadingPhrases: false,
1087
- screenReader: false,
959
+ systemSettings: {},
960
+ workspaceSettings: {},
961
+ stdinActions: undefined,
962
+ },
963
+ {
964
+ name: 'tools and security settings',
965
+ userSettings: {
966
+ tools: {
967
+ enableInteractiveShell: true,
968
+ autoAccept: false,
969
+ useRipgrep: true,
970
+ truncateToolOutputThreshold: 25000,
971
+ truncateToolOutputLines: 500,
1088
972
  },
1089
- },
1090
- ide: {
1091
- enabled: false,
1092
- },
1093
- context: {
1094
- loadMemoryFromIncludeDirectories: false,
1095
- fileFiltering: {
1096
- respectGitIgnore: false,
1097
- respectGeminiIgnore: false,
1098
- enableRecursiveFileSearch: false,
1099
- disableFuzzySearch: false,
973
+ security: {
974
+ folderTrust: {
975
+ enabled: true,
976
+ },
977
+ },
978
+ model: {
979
+ maxSessionTurns: 50,
980
+ skipNextSpeakerCheck: true,
1100
981
  },
1101
982
  },
1102
- tools: {
1103
- enableInteractiveShell: false,
1104
- autoAccept: false,
1105
- useRipgrep: false,
1106
- },
1107
- security: {
1108
- folderTrust: {
983
+ systemSettings: {},
984
+ workspaceSettings: {},
985
+ stdinActions: undefined,
986
+ },
987
+ {
988
+ name: 'all boolean settings disabled',
989
+ userSettings: {
990
+ general: {
991
+ vimMode: false,
992
+ disableAutoUpdate: false,
993
+ debugKeystrokeLogging: false,
994
+ enablePromptCompletion: false,
995
+ },
996
+ ui: {
997
+ hideWindowTitle: false,
998
+ hideTips: false,
999
+ showMemoryUsage: false,
1000
+ showLineNumbers: false,
1001
+ showCitations: false,
1002
+ accessibility: {
1003
+ disableLoadingPhrases: false,
1004
+ screenReader: false,
1005
+ },
1006
+ },
1007
+ ide: {
1109
1008
  enabled: false,
1110
1009
  },
1010
+ context: {
1011
+ loadMemoryFromIncludeDirectories: false,
1012
+ fileFiltering: {
1013
+ respectGitIgnore: false,
1014
+ respectGeminiIgnore: false,
1015
+ enableRecursiveFileSearch: false,
1016
+ disableFuzzySearch: false,
1017
+ },
1018
+ },
1019
+ tools: {
1020
+ enableInteractiveShell: false,
1021
+ autoAccept: false,
1022
+ useRipgrep: false,
1023
+ },
1024
+ security: {
1025
+ folderTrust: {
1026
+ enabled: false,
1027
+ },
1028
+ },
1111
1029
  },
1112
- });
1030
+ systemSettings: {},
1031
+ workspaceSettings: {},
1032
+ stdinActions: undefined,
1033
+ },
1034
+ ])('should render $name correctly', ({ userSettings, systemSettings, workspaceSettings, stdinActions }) => {
1035
+ const settings = createMockSettings(userSettings, systemSettings, workspaceSettings);
1113
1036
  const onSelect = vi.fn();
1114
- const { lastFrame } = render(_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: _jsx(SettingsDialog, { settings: settings, onSelect: onSelect }) }));
1037
+ const { lastFrame, stdin } = renderDialog(settings, onSelect);
1038
+ if (stdinActions) {
1039
+ stdinActions(stdin);
1040
+ }
1115
1041
  expect(lastFrame()).toMatchSnapshot();
1116
1042
  });
1117
1043
  });