@shopify/cli-kit 3.34.0 → 3.36.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 (241) hide show
  1. package/dist/content-tokens.js +1 -1
  2. package/dist/content-tokens.js.map +1 -1
  3. package/dist/error.js +2 -2
  4. package/dist/error.js.map +1 -1
  5. package/dist/git.js +2 -2
  6. package/dist/git.js.map +1 -1
  7. package/dist/index.d.ts +0 -6
  8. package/dist/index.js +0 -6
  9. package/dist/index.js.map +1 -1
  10. package/dist/metadata.d.ts +1 -1
  11. package/dist/metadata.js.map +1 -1
  12. package/dist/monorail.d.ts +1 -1
  13. package/dist/monorail.js +1 -1
  14. package/dist/monorail.js.map +1 -1
  15. package/dist/output.js +3 -7
  16. package/dist/output.js.map +1 -1
  17. package/dist/plugins.d.ts +1 -1
  18. package/dist/plugins.js.map +1 -1
  19. package/dist/{typing → private/common/ts}/deep-required.d.ts +3 -3
  20. package/dist/{typing → private/common/ts}/deep-required.js +0 -0
  21. package/dist/private/common/ts/deep-required.js.map +1 -0
  22. package/dist/{typing → private/common/ts}/overloaded-parameters.d.ts +0 -0
  23. package/dist/{typing → private/common/ts}/overloaded-parameters.js +0 -0
  24. package/dist/private/common/ts/overloaded-parameters.js.map +1 -0
  25. package/dist/private/node/api/graphql.js +2 -3
  26. package/dist/private/node/api/graphql.js.map +1 -1
  27. package/dist/private/node/api/headers.d.ts +11 -2
  28. package/dist/private/node/api/headers.js +29 -3
  29. package/dist/private/node/api/headers.js.map +1 -1
  30. package/dist/private/node/api/rest.d.ts +5 -3
  31. package/dist/private/node/api/rest.js +8 -7
  32. package/dist/private/node/api/rest.js.map +1 -1
  33. package/dist/private/node/constants.d.ts +42 -0
  34. package/dist/private/node/constants.js +58 -0
  35. package/dist/private/node/constants.js.map +1 -0
  36. package/dist/private/node/environment/service.js +2 -2
  37. package/dist/private/node/environment/service.js.map +1 -1
  38. package/dist/private/node/session/device-authorization.js +1 -1
  39. package/dist/private/node/session/device-authorization.js.map +1 -1
  40. package/dist/private/node/session/exchange.js +1 -1
  41. package/dist/private/node/session/exchange.js.map +1 -1
  42. package/dist/private/node/session/identity-token-validation.js +1 -1
  43. package/dist/private/node/session/identity-token-validation.js.map +1 -1
  44. package/dist/private/node/session/post-auth.js +10 -10
  45. package/dist/private/node/session/post-auth.js.map +1 -1
  46. package/dist/private/node/session/store.js +6 -6
  47. package/dist/private/node/session/store.js.map +1 -1
  48. package/dist/private/node/session/validate.js +2 -2
  49. package/dist/private/node/session/validate.js.map +1 -1
  50. package/dist/private/node/session.js +2 -2
  51. package/dist/private/node/session.js.map +1 -1
  52. package/dist/{typing → private/node}/simple-definitions.d.ts +0 -0
  53. package/dist/{typing → private/node}/simple-definitions.js +0 -0
  54. package/dist/private/node/simple-definitions.js.map +1 -0
  55. package/dist/private/node/ui/alert.d.ts +1 -1
  56. package/dist/private/node/ui/alert.js +2 -2
  57. package/dist/private/node/ui/alert.js.map +1 -1
  58. package/dist/private/node/ui/components/Alert.d.ts +5 -0
  59. package/dist/private/node/ui/components/Alert.js +9 -6
  60. package/dist/private/node/ui/components/Alert.js.map +1 -1
  61. package/dist/private/node/ui/components/Alert.test.js +30 -4
  62. package/dist/private/node/ui/components/Alert.test.js.map +1 -1
  63. package/dist/private/node/ui/components/AutocompletePrompt.d.ts +12 -0
  64. package/dist/private/node/ui/components/AutocompletePrompt.js +111 -0
  65. package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -0
  66. package/dist/private/node/ui/components/AutocompletePrompt.test.d.ts +1 -0
  67. package/dist/private/node/ui/components/AutocompletePrompt.test.js +473 -0
  68. package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -0
  69. package/dist/private/node/ui/components/Banner.d.ts +0 -1
  70. package/dist/private/node/ui/components/Banner.js +4 -4
  71. package/dist/private/node/ui/components/Banner.js.map +1 -1
  72. package/dist/private/node/ui/components/Banner.test.js +10 -5
  73. package/dist/private/node/ui/components/Banner.test.js.map +1 -1
  74. package/dist/private/node/ui/components/FatalError.js +1 -1
  75. package/dist/private/node/ui/components/FatalError.js.map +1 -1
  76. package/dist/private/node/ui/components/FatalError.test.js +4 -8
  77. package/dist/private/node/ui/components/FatalError.test.js.map +1 -1
  78. package/dist/private/node/ui/components/{Table.d.ts → Prompts/InfoTable.d.ts} +2 -2
  79. package/dist/private/node/ui/components/{Table.js → Prompts/InfoTable.js} +6 -6
  80. package/dist/private/node/ui/components/Prompts/InfoTable.js.map +1 -0
  81. package/dist/private/node/ui/components/SelectInput.d.ts +9 -2
  82. package/dist/private/node/ui/components/SelectInput.js +96 -52
  83. package/dist/private/node/ui/components/SelectInput.js.map +1 -1
  84. package/dist/private/node/ui/components/SelectInput.test.js +140 -54
  85. package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
  86. package/dist/private/node/ui/components/SelectPrompt.d.ts +6 -4
  87. package/dist/private/node/ui/components/SelectPrompt.js +18 -11
  88. package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
  89. package/dist/private/node/ui/components/SelectPrompt.test.js +113 -23
  90. package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
  91. package/dist/private/node/ui/components/Table/Column.d.ts +5 -0
  92. package/dist/private/node/ui/components/Table/Column.js +2 -0
  93. package/dist/private/node/ui/components/Table/Column.js.map +1 -0
  94. package/dist/private/node/ui/components/Table/Row.d.ts +12 -0
  95. package/dist/private/node/ui/components/Table/Row.js +24 -0
  96. package/dist/private/node/ui/components/Table/Row.js.map +1 -0
  97. package/dist/private/node/ui/components/Table/ScalarDict.d.ts +5 -0
  98. package/dist/private/node/ui/components/Table/ScalarDict.js +2 -0
  99. package/dist/private/node/ui/components/Table/ScalarDict.js.map +1 -0
  100. package/dist/private/node/ui/components/Table/Table.d.ts +12 -0
  101. package/dist/private/node/ui/components/Table/Table.js +30 -0
  102. package/dist/private/node/ui/components/Table/Table.js.map +1 -0
  103. package/dist/private/node/ui/components/Table/Table.test.d.ts +1 -0
  104. package/dist/private/node/ui/components/Table/Table.test.js +41 -0
  105. package/dist/private/node/ui/components/Table/Table.test.js.map +1 -0
  106. package/dist/private/node/ui/components/Tasks.d.ts +6 -5
  107. package/dist/private/node/ui/components/Tasks.js +32 -11
  108. package/dist/private/node/ui/components/Tasks.js.map +1 -1
  109. package/dist/private/node/ui/components/Tasks.test.js +55 -9
  110. package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
  111. package/dist/private/node/ui/components/TextInput.d.ts +4 -1
  112. package/dist/private/node/ui/components/TextInput.js +22 -13
  113. package/dist/private/node/ui/components/TextInput.js.map +1 -1
  114. package/dist/private/node/ui/components/TextInput.test.js +47 -40
  115. package/dist/private/node/ui/components/TextInput.test.js.map +1 -1
  116. package/dist/private/node/ui/components/TextPrompt.d.ts +3 -1
  117. package/dist/private/node/ui/components/TextPrompt.js +28 -15
  118. package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
  119. package/dist/private/node/ui/components/TextPrompt.test.js +71 -15
  120. package/dist/private/node/ui/components/TextPrompt.test.js.map +1 -1
  121. package/dist/private/node/ui/components/TokenizedText.d.ts +3 -0
  122. package/dist/private/node/ui/components/TokenizedText.js +33 -1
  123. package/dist/private/node/ui/components/TokenizedText.js.map +1 -1
  124. package/dist/private/node/ui/utilities.d.ts +2 -0
  125. package/dist/private/node/ui/utilities.js +6 -0
  126. package/dist/private/node/ui/utilities.js.map +1 -0
  127. package/dist/public/common/string.d.ts +11 -0
  128. package/dist/public/common/string.js +21 -0
  129. package/dist/public/common/string.js.map +1 -1
  130. package/dist/{typing → public/common/ts}/pick-by-prefix.d.ts +4 -3
  131. package/dist/{typing → public/common/ts}/pick-by-prefix.js +0 -0
  132. package/dist/public/common/ts/pick-by-prefix.js.map +1 -0
  133. package/dist/public/common/version.d.ts +1 -0
  134. package/dist/public/common/version.js +2 -0
  135. package/dist/public/common/version.js.map +1 -0
  136. package/dist/public/node/analytics.js +2 -2
  137. package/dist/public/node/analytics.js.map +1 -1
  138. package/dist/public/node/api/admin.d.ts +4 -1
  139. package/dist/public/node/api/admin.js +5 -4
  140. package/dist/public/node/api/admin.js.map +1 -1
  141. package/dist/public/node/api/http.d.ts +0 -0
  142. package/dist/public/node/api/http.js +2 -0
  143. package/dist/public/node/api/http.js.map +1 -0
  144. package/dist/public/node/api/oxygen.js +1 -1
  145. package/dist/public/node/api/oxygen.js.map +1 -1
  146. package/dist/public/node/archiver.js +2 -1
  147. package/dist/public/node/archiver.js.map +1 -1
  148. package/dist/public/node/cli.js +6 -6
  149. package/dist/public/node/cli.js.map +1 -1
  150. package/dist/public/node/dot-env.js +2 -2
  151. package/dist/public/node/dot-env.js.map +1 -1
  152. package/dist/public/node/environment/local.js +17 -17
  153. package/dist/public/node/environment/local.js.map +1 -1
  154. package/dist/public/node/environment/spin.js +6 -6
  155. package/dist/public/node/environment/spin.js.map +1 -1
  156. package/dist/public/node/error-handler.js +13 -10
  157. package/dist/public/node/error-handler.js.map +1 -1
  158. package/dist/public/node/framework.js +2 -2
  159. package/dist/public/node/framework.js.map +1 -1
  160. package/dist/public/node/fs.d.ts +241 -4
  161. package/dist/public/node/fs.js +366 -2
  162. package/dist/public/node/fs.js.map +1 -1
  163. package/dist/public/node/git.d.ts +90 -0
  164. package/dist/public/node/git.js +174 -0
  165. package/dist/public/node/git.js.map +1 -0
  166. package/dist/public/node/github.js +1 -1
  167. package/dist/public/node/github.js.map +1 -1
  168. package/dist/{http/fetch.d.ts → public/node/http.d.ts} +18 -7
  169. package/dist/{http/fetch.js → public/node/http.js} +21 -8
  170. package/dist/public/node/http.js.map +1 -0
  171. package/dist/public/node/liquid.js +9 -9
  172. package/dist/public/node/liquid.js.map +1 -1
  173. package/dist/public/node/node-package-manager.d.ts +20 -9
  174. package/dist/public/node/node-package-manager.js +19 -25
  175. package/dist/public/node/node-package-manager.js.map +1 -1
  176. package/dist/public/node/path.d.ts +22 -0
  177. package/dist/{path.js → public/node/path.js} +8 -20
  178. package/dist/public/node/path.js.map +1 -0
  179. package/dist/public/node/presets.js +5 -5
  180. package/dist/public/node/presets.js.map +1 -1
  181. package/dist/public/node/ruby.js +29 -33
  182. package/dist/public/node/ruby.js.map +1 -1
  183. package/dist/public/node/session.js +2 -2
  184. package/dist/public/node/session.js.map +1 -1
  185. package/dist/public/node/ui.d.ts +63 -9
  186. package/dist/public/node/ui.js +83 -8
  187. package/dist/public/node/ui.js.map +1 -1
  188. package/dist/public/node/vscode.js +8 -8
  189. package/dist/public/node/vscode.js.map +1 -1
  190. package/dist/secure-store.js +4 -4
  191. package/dist/secure-store.js.map +1 -1
  192. package/dist/store.d.ts +10 -10
  193. package/dist/store.js +21 -22
  194. package/dist/store.js.map +1 -1
  195. package/dist/testing/store.js +3 -3
  196. package/dist/testing/store.js.map +1 -1
  197. package/dist/testing/ui.d.ts +4 -1
  198. package/dist/testing/ui.js +24 -1
  199. package/dist/testing/ui.js.map +1 -1
  200. package/dist/tsconfig.tsbuildinfo +1 -1
  201. package/dist/ui/executor.d.ts +2 -14
  202. package/dist/ui/executor.js +38 -72
  203. package/dist/ui/executor.js.map +1 -1
  204. package/dist/ui.js +9 -26
  205. package/dist/ui.js.map +1 -1
  206. package/package.json +4 -6
  207. package/dist/constants.d.ts +0 -48
  208. package/dist/constants.js +0 -67
  209. package/dist/constants.js.map +0 -1
  210. package/dist/file.d.ts +0 -98
  211. package/dist/file.js +0 -216
  212. package/dist/file.js.map +0 -1
  213. package/dist/http/fetch.js.map +0 -1
  214. package/dist/http/formdata.d.ts +0 -3
  215. package/dist/http/formdata.js +0 -6
  216. package/dist/http/formdata.js.map +0 -1
  217. package/dist/http.d.ts +0 -26
  218. package/dist/http.js +0 -31
  219. package/dist/http.js.map +0 -1
  220. package/dist/npm.d.ts +0 -27
  221. package/dist/npm.js +0 -20
  222. package/dist/npm.js.map +0 -1
  223. package/dist/path.d.ts +0 -25
  224. package/dist/path.js.map +0 -1
  225. package/dist/private/node/ui/components/Table.js.map +0 -1
  226. package/dist/typing/deep-required.js.map +0 -1
  227. package/dist/typing/overloaded-parameters.js.map +0 -1
  228. package/dist/typing/pick-by-prefix.js.map +0 -1
  229. package/dist/typing/simple-definitions.js.map +0 -1
  230. package/dist/ui/inquirer/autocomplete.d.ts +0 -11
  231. package/dist/ui/inquirer/autocomplete.js +0 -110
  232. package/dist/ui/inquirer/autocomplete.js.map +0 -1
  233. package/dist/ui/inquirer/input.d.ts +0 -16
  234. package/dist/ui/inquirer/input.js +0 -45
  235. package/dist/ui/inquirer/input.js.map +0 -1
  236. package/dist/ui/inquirer/password.d.ts +0 -7
  237. package/dist/ui/inquirer/password.js +0 -8
  238. package/dist/ui/inquirer/password.js.map +0 -1
  239. package/dist/ui/inquirer/select.d.ts +0 -14
  240. package/dist/ui/inquirer/select.js +0 -26
  241. package/dist/ui/inquirer/select.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { TextInput } from './TextInput.js';
2
- import { sendInput, waitForChange, waitForInputsToBeReady } from '../../../../testing/ui.js';
2
+ import { sendInputAndWait, sendInputAndWaitForChange, waitForChange, waitForInputsToBeReady, } from '../../../../testing/ui.js';
3
3
  import React, { useState } from 'react';
4
4
  import { describe, test, expect, vi } from 'vitest';
5
5
  import { render } from 'ink-testing-library';
@@ -26,33 +26,31 @@ describe('TextInput', () => {
26
26
  const renderInstance = render(React.createElement(TextInput, { value: "Hello", onChange: () => { } }));
27
27
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
28
28
  await waitForInputsToBeReady();
29
- await sendInput(renderInstance, ARROW_LEFT);
29
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
30
30
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
31
- await sendInput(renderInstance, ARROW_LEFT);
31
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
32
32
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
33
- await sendInput(renderInstance, ARROW_LEFT);
33
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
34
34
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
35
- await sendInput(renderInstance, ARROW_LEFT);
35
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
36
36
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
37
- await sendInput(renderInstance, ARROW_LEFT);
37
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
38
38
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
39
39
  // cursor can't go before the first character
40
- renderInstance.stdin.write(ARROW_LEFT);
41
- await new Promise((resolve) => setTimeout(resolve, 100));
40
+ await sendInputAndWait(renderInstance, 100, ARROW_LEFT);
42
41
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
43
- await sendInput(renderInstance, ARROW_RIGHT);
42
+ await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT);
44
43
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
45
- await sendInput(renderInstance, ARROW_RIGHT);
44
+ await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT);
46
45
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
47
- await sendInput(renderInstance, ARROW_RIGHT);
46
+ await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT);
48
47
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
49
- await sendInput(renderInstance, ARROW_RIGHT);
48
+ await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT);
50
49
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"');
51
- await sendInput(renderInstance, ARROW_RIGHT);
50
+ await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT);
52
51
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
53
52
  // cursor can't go after the last character
54
- renderInstance.stdin.write(ARROW_RIGHT);
55
- await new Promise((resolve) => setTimeout(resolve, 100));
53
+ await sendInputAndWait(renderInstance, 100, ARROW_RIGHT);
56
54
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
57
55
  });
58
56
  test('moves the cursor when deleting', async () => {
@@ -63,19 +61,18 @@ describe('TextInput', () => {
63
61
  const renderInstance = render(React.createElement(StatefulTextInput, null));
64
62
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
65
63
  await waitForInputsToBeReady();
66
- await sendInput(renderInstance, DELETE);
64
+ await sendInputAndWaitForChange(renderInstance, DELETE);
67
65
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hell "');
68
- await sendInput(renderInstance, DELETE);
66
+ await sendInputAndWaitForChange(renderInstance, DELETE);
69
67
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hel "');
70
- await sendInput(renderInstance, DELETE);
68
+ await sendInputAndWaitForChange(renderInstance, DELETE);
71
69
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"He "');
72
- await sendInput(renderInstance, DELETE);
70
+ await sendInputAndWaitForChange(renderInstance, DELETE);
73
71
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"H "');
74
- await sendInput(renderInstance, DELETE);
72
+ await sendInputAndWaitForChange(renderInstance, DELETE);
75
73
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('" "');
76
74
  // cannot delete after the value has been cleared
77
- renderInstance.stdin.write(DELETE);
78
- await new Promise((resolve) => setTimeout(resolve, 100));
75
+ await sendInputAndWait(renderInstance, 100, DELETE);
79
76
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('" "');
80
77
  });
81
78
  test('accepts input', async () => {
@@ -85,16 +82,23 @@ describe('TextInput', () => {
85
82
  };
86
83
  const renderInstance = render(React.createElement(StatefulTextInput, null));
87
84
  await waitForInputsToBeReady();
88
- await sendInput(renderInstance, 'H');
85
+ await sendInputAndWaitForChange(renderInstance, 'H');
89
86
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"H "');
90
- await sendInput(renderInstance, 'ello');
87
+ await sendInputAndWaitForChange(renderInstance, 'ello');
91
88
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello "');
92
89
  });
93
90
  test('onChange', async () => {
94
91
  const onChange = vi.fn();
95
- const renderInstance = render(React.createElement(TextInput, { value: "", onChange: onChange }));
92
+ const StatefulTextInput = () => {
93
+ const [value, setValue] = useState('');
94
+ return (React.createElement(TextInput, { value: value, onChange: (value) => {
95
+ setValue(value);
96
+ onChange(value);
97
+ } }));
98
+ };
99
+ const renderInstance = render(React.createElement(StatefulTextInput, null));
96
100
  await waitForInputsToBeReady();
97
- await sendInput(renderInstance, 'X');
101
+ await sendInputAndWaitForChange(renderInstance, 'X');
98
102
  expect(onChange).toHaveBeenCalledWith('X');
99
103
  });
100
104
  test('deletes at the beginning and in the middle of text', async () => {
@@ -104,17 +108,16 @@ describe('TextInput', () => {
104
108
  };
105
109
  const renderInstance = render(React.createElement(StatefulTextInput, null));
106
110
  await waitForInputsToBeReady();
107
- await sendInput(renderInstance, 'T');
108
- await sendInput(renderInstance, 'e');
109
- await sendInput(renderInstance, 's');
110
- await sendInput(renderInstance, 't');
111
- await sendInput(renderInstance, ARROW_LEFT);
112
- await sendInput(renderInstance, DELETE);
111
+ await sendInputAndWaitForChange(renderInstance, 'T');
112
+ await sendInputAndWaitForChange(renderInstance, 'e');
113
+ await sendInputAndWaitForChange(renderInstance, 's');
114
+ await sendInputAndWaitForChange(renderInstance, 't');
115
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
116
+ await sendInputAndWaitForChange(renderInstance, DELETE);
113
117
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Tet"');
114
- await sendInput(renderInstance, ARROW_LEFT);
115
- await sendInput(renderInstance, ARROW_LEFT);
116
- renderInstance.stdin.write(DELETE);
117
- await new Promise((resolve) => setTimeout(resolve, 100));
118
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
119
+ await sendInputAndWaitForChange(renderInstance, ARROW_LEFT);
120
+ await sendInputAndWait(renderInstance, 100, DELETE);
118
121
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Tet"');
119
122
  });
120
123
  test('adjusts cursor when text is shorter than last value', async () => {
@@ -126,14 +129,18 @@ describe('TextInput', () => {
126
129
  };
127
130
  const renderInstance = render(React.createElement(StatefulTextInput, null));
128
131
  await waitForInputsToBeReady();
129
- await sendInput(renderInstance, 'A');
130
- await sendInput(renderInstance, 'B');
132
+ await sendInputAndWaitForChange(renderInstance, 'A');
133
+ await sendInputAndWaitForChange(renderInstance, 'B');
131
134
  await waitForChange(resetValue, renderInstance.lastFrame);
132
135
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('" "');
133
- await sendInput(renderInstance, 'A');
136
+ await sendInputAndWaitForChange(renderInstance, 'A');
134
137
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"A "');
135
- await sendInput(renderInstance, 'B');
138
+ await sendInputAndWaitForChange(renderInstance, 'B');
136
139
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"AB "');
137
140
  });
141
+ test("masking the input if it's a password", async () => {
142
+ const renderInstance = render(React.createElement(TextInput, { onChange: () => { }, value: "ABC", password: true }));
143
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"*** "');
144
+ });
138
145
  });
139
146
  //# sourceMappingURL=TextInput.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextInput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextInput.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {sendInput, waitForChange, waitForInputsToBeReady} from '../../../../testing/ui.js'\nimport React, {useState} from 'react'\nimport {describe, test, expect, vi} from 'vitest'\nimport {render} from 'ink-testing-library'\n\nconst ARROW_LEFT = '\\u001B[D'\nconst ARROW_RIGHT = '\\u001B[C'\nconst DELETE = '\\u007F'\n\ndescribe('TextInput', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextInput value=\"\" onChange={() => {}} />)\n\n // inverted space escape sequence\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('displays value with cursor', () => {\n const {lastFrame} = render(<TextInput value=\"Hello\" onChange={() => {}} />)\n\n // inverted space escape sequence after Hello\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('displays placeholder', () => {\n const {lastFrame} = render(<TextInput value=\"\" placeholder=\"Placeholder\" onChange={() => {}} />)\n\n // inverted escape sequence around \"P\", laceholder after that\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mP\u001b[27m\u001b[2mlaceholder\u001b[22m\u001b[39m\"')\n })\n\n test('moves the cursor with arrows', async () => {\n const renderInstance = render(<TextInput value=\"Hello\" onChange={() => {}} />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7mo\u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7ml\u001b[27mo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7ml\u001b[27mlo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7me\u001b[27mllo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mH\u001b[27mello\u001b[39m\"')\n // cursor can't go before the first character\n renderInstance.stdin.write(ARROW_LEFT)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mH\u001b[27mello\u001b[39m\"')\n\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7me\u001b[27mllo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7ml\u001b[27mlo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7ml\u001b[27mo\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7mo\u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n // cursor can't go after the last character\n renderInstance.stdin.write(ARROW_RIGHT)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('moves the cursor when deleting', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('Hello')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n // cannot delete after the value has been cleared\n renderInstance.stdin.write(DELETE)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('accepts input', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'H')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, 'ello')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('onChange', async () => {\n const onChange = vi.fn()\n\n const renderInstance = render(<TextInput value=\"\" onChange={onChange} />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'X')\n\n expect(onChange).toHaveBeenCalledWith('X')\n })\n\n test('deletes at the beginning and in the middle of text', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'T')\n await sendInput(renderInstance, 'e')\n await sendInput(renderInstance, 's')\n await sendInput(renderInstance, 't')\n await sendInput(renderInstance, ARROW_LEFT)\n await sendInput(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mTe\u001b[7mt\u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, ARROW_LEFT)\n await sendInput(renderInstance, ARROW_LEFT)\n renderInstance.stdin.write(DELETE)\n await new Promise((resolve) => setTimeout(resolve, 100))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mT\u001b[27met\u001b[39m\"')\n })\n\n test('adjusts cursor when text is shorter than last value', async () => {\n let resetValue = () => {}\n\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n resetValue = () => setValue('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'A')\n await sendInput(renderInstance, 'B')\n\n await waitForChange(resetValue, renderInstance.lastFrame)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, 'A')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mA\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInput(renderInstance, 'B')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mAB\u001b[7m \u001b[27m\u001b[39m\"')\n })\n})\n"]}
1
+ {"version":3,"file":"TextInput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextInput.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {\n sendInputAndWait,\n sendInputAndWaitForChange,\n waitForChange,\n waitForInputsToBeReady,\n} from '../../../../testing/ui.js'\nimport React, {useState} from 'react'\nimport {describe, test, expect, vi} from 'vitest'\nimport {render} from 'ink-testing-library'\n\nconst ARROW_LEFT = '\\u001B[D'\nconst ARROW_RIGHT = '\\u001B[C'\nconst DELETE = '\\u007F'\n\ndescribe('TextInput', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextInput value=\"\" onChange={() => {}} />)\n\n // inverted space escape sequence\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('displays value with cursor', () => {\n const {lastFrame} = render(<TextInput value=\"Hello\" onChange={() => {}} />)\n\n // inverted space escape sequence after Hello\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('displays placeholder', () => {\n const {lastFrame} = render(<TextInput value=\"\" placeholder=\"Placeholder\" onChange={() => {}} />)\n\n // inverted escape sequence around \"P\", laceholder after that\n expect(lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mP\u001b[27m\u001b[2mlaceholder\u001b[22m\u001b[39m\"')\n })\n\n test('moves the cursor with arrows', async () => {\n const renderInstance = render(<TextInput value=\"Hello\" onChange={() => {}} />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7mo\u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7ml\u001b[27mo\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7ml\u001b[27mlo\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7me\u001b[27mllo\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mH\u001b[27mello\u001b[39m\"')\n // cursor can't go before the first character\n await sendInputAndWait(renderInstance, 100, ARROW_LEFT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mH\u001b[27mello\u001b[39m\"')\n\n await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7me\u001b[27mllo\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7ml\u001b[27mlo\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7ml\u001b[27mo\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7mo\u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n // cursor can't go after the last character\n await sendInputAndWait(renderInstance, 100, ARROW_RIGHT)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('moves the cursor when deleting', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('Hello')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHell\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHel\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHe\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n // cannot delete after the value has been cleared\n await sendInputAndWait(renderInstance, 100, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('accepts input', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'H')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mH\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, 'ello')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mHello\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test('onChange', async () => {\n const onChange = vi.fn()\n\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return (\n <TextInput\n value={value}\n onChange={(value) => {\n setValue(value)\n onChange(value)\n }}\n />\n )\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'X')\n\n expect(onChange).toHaveBeenCalledWith('X')\n })\n\n test('deletes at the beginning and in the middle of text', async () => {\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'T')\n await sendInputAndWaitForChange(renderInstance, 'e')\n await sendInputAndWaitForChange(renderInstance, 's')\n await sendInputAndWaitForChange(renderInstance, 't')\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n await sendInputAndWaitForChange(renderInstance, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mTe\u001b[7mt\u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n await sendInputAndWaitForChange(renderInstance, ARROW_LEFT)\n await sendInputAndWait(renderInstance, 100, DELETE)\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7mT\u001b[27met\u001b[39m\"')\n })\n\n test('adjusts cursor when text is shorter than last value', async () => {\n let resetValue = () => {}\n\n const StatefulTextInput = () => {\n const [value, setValue] = useState('')\n resetValue = () => setValue('')\n\n return <TextInput value={value} onChange={setValue} />\n }\n\n const renderInstance = render(<StatefulTextInput />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A')\n await sendInputAndWaitForChange(renderInstance, 'B')\n\n await waitForChange(resetValue, renderInstance.lastFrame)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, 'A')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mA\u001b[7m \u001b[27m\u001b[39m\"')\n await sendInputAndWaitForChange(renderInstance, 'B')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36mAB\u001b[7m \u001b[27m\u001b[39m\"')\n })\n\n test(\"masking the input if it's a password\", async () => {\n const renderInstance = render(<TextInput onChange={() => {}} value=\"ABC\" password />)\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot('\"\u001b[36m***\u001b[7m \u001b[27m\u001b[39m\"')\n })\n})\n"]}
@@ -2,7 +2,9 @@ import React from 'react';
2
2
  export interface Props {
3
3
  message: string;
4
4
  onSubmit: (value: string) => void;
5
- placeholder?: string;
5
+ defaultValue?: string;
6
+ password?: boolean;
7
+ validate?: (value: string) => string | undefined;
6
8
  }
7
9
  declare const TextPrompt: React.FC<Props>;
8
10
  export { TextPrompt };
@@ -1,51 +1,64 @@
1
1
  import { TextInput } from './TextInput.js';
2
2
  import { handleCtrlC } from '../../ui.js';
3
3
  import useLayout from '../hooks/use-layout.js';
4
+ import { messageWithPunctuation } from '../utilities.js';
4
5
  import React, { useCallback, useState } from 'react';
5
6
  import { Box, useApp, useInput, Text } from 'ink';
6
- import { figures } from 'listr2';
7
- const TextPrompt = ({ message, onSubmit, placeholder }) => {
7
+ import figures from 'figures';
8
+ const TextPrompt = ({ message, onSubmit, validate, defaultValue = '', password = false }) => {
9
+ if (password && defaultValue) {
10
+ throw new Error("Can't use defaultValue with password");
11
+ }
12
+ const validateAnswer = (value) => {
13
+ if (validate) {
14
+ return validate(value);
15
+ }
16
+ if (value.length === 0)
17
+ return 'Type an answer to the prompt.';
18
+ return undefined;
19
+ };
8
20
  const { oneThird } = useLayout();
9
21
  const [answer, setAnswer] = useState('');
22
+ const answerOrDefault = answer.length > 0 ? answer : defaultValue;
10
23
  const { exit: unmountInk } = useApp();
11
24
  const [submitted, setSubmitted] = useState(false);
12
- const [valid, setValid] = useState(false);
25
+ const [error, setError] = useState(undefined);
26
+ const shouldShowError = submitted && error;
27
+ const color = shouldShowError ? 'red' : 'cyan';
13
28
  const underline = new Array(oneThird - 3).fill('▔');
14
29
  useInput(useCallback((input, key) => {
15
30
  handleCtrlC(input, key);
16
31
  if (key.return) {
17
32
  setSubmitted(true);
18
- if (valid) {
19
- onSubmit(answer);
33
+ const error = validateAnswer(answerOrDefault);
34
+ setError(error);
35
+ if (!error) {
36
+ onSubmit(answerOrDefault);
20
37
  unmountInk();
21
38
  }
22
39
  }
23
- }, [answer, onSubmit, valid]));
24
- const shouldShowError = submitted && !valid;
25
- const color = shouldShowError ? 'red' : 'cyan';
26
- const error = shouldShowError ? 'Please enter a value' : undefined;
40
+ }, [answerOrDefault, onSubmit]));
27
41
  return (React.createElement(Box, { flexDirection: "column", marginBottom: 1, width: oneThird },
28
42
  React.createElement(Box, null,
29
43
  React.createElement(Box, { marginRight: 2 },
30
44
  React.createElement(Text, null, "?")),
31
- React.createElement(Text, null, message)),
32
- submitted && valid ? (React.createElement(Box, null,
45
+ React.createElement(Text, null, messageWithPunctuation(message))),
46
+ submitted && !error ? (React.createElement(Box, null,
33
47
  React.createElement(Box, { marginRight: 2 },
34
48
  React.createElement(Text, { color: "cyan" }, figures.tick)),
35
49
  React.createElement(Box, { flexGrow: 1 },
36
- React.createElement(Text, { color: "cyan" }, answer)))) : (React.createElement(Box, { flexDirection: "column" },
50
+ React.createElement(Text, { color: "cyan" }, password ? '*'.repeat(answer.length) : answerOrDefault)))) : (React.createElement(Box, { flexDirection: "column" },
37
51
  React.createElement(Box, null,
38
52
  React.createElement(Box, { marginRight: 2 },
39
53
  React.createElement(Text, { color: color }, `>`)),
40
54
  React.createElement(Box, { flexGrow: 1 },
41
55
  React.createElement(TextInput, { value: answer, onChange: (answer) => {
42
56
  setAnswer(answer);
43
- setValid(answer.length > 0);
44
57
  setSubmitted(false);
45
- }, placeholder: placeholder, color: color }))),
58
+ }, defaultValue: defaultValue, color: color, password: password }))),
46
59
  React.createElement(Box, { marginLeft: 3 },
47
60
  React.createElement(Text, { color: color }, underline)),
48
- error && (React.createElement(Box, { marginLeft: 3 },
61
+ shouldShowError && (React.createElement(Box, { marginLeft: 3 },
49
62
  React.createElement(Text, { color: color }, error)))))));
50
63
  };
51
64
  export { TextPrompt };
@@ -1 +1 @@
1
- {"version":3,"file":"TextPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {handleCtrlC} from '../../ui.js'\nimport useLayout from '../hooks/use-layout.js'\nimport React, {useCallback, useState} from 'react'\nimport {Box, useApp, useInput, Text} from 'ink'\nimport {figures} from 'listr2'\n\nexport interface Props {\n message: string\n onSubmit: (value: string) => void\n placeholder?: string\n}\n\nconst TextPrompt: React.FC<Props> = ({message, onSubmit, placeholder}) => {\n const {oneThird} = useLayout()\n const [answer, setAnswer] = useState<string>('')\n const {exit: unmountInk} = useApp()\n const [submitted, setSubmitted] = useState(false)\n const [valid, setValid] = useState(false)\n const underline = new Array(oneThird - 3).fill('▔')\n\n useInput(\n useCallback(\n (input, key) => {\n handleCtrlC(input, key)\n\n if (key.return) {\n setSubmitted(true)\n\n if (valid) {\n onSubmit(answer)\n unmountInk()\n }\n }\n },\n [answer, onSubmit, valid],\n ),\n )\n\n const shouldShowError = submitted && !valid\n const color = shouldShowError ? 'red' : 'cyan'\n const error = shouldShowError ? 'Please enter a value' : undefined\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} width={oneThird}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <Text>{message}</Text>\n </Box>\n {submitted && valid ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Box flexGrow={1}>\n <Text color=\"cyan\">{answer}</Text>\n </Box>\n </Box>\n ) : (\n <Box flexDirection=\"column\">\n <Box>\n <Box marginRight={2}>\n <Text color={color}>{`>`}</Text>\n </Box>\n <Box flexGrow={1}>\n <TextInput\n value={answer}\n onChange={(answer) => {\n setAnswer(answer)\n setValid(answer.length > 0)\n setSubmitted(false)\n }}\n placeholder={placeholder}\n color={color}\n />\n </Box>\n </Box>\n <Box marginLeft={3}>\n <Text color={color}>{underline}</Text>\n </Box>\n {error && (\n <Box marginLeft={3}>\n <Text color={color}>{error}</Text>\n </Box>\n )}\n </Box>\n )}\n </Box>\n )\n}\n\nexport {TextPrompt}\n"]}
1
+ {"version":3,"file":"TextPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextInput} from './TextInput.js'\nimport {handleCtrlC} from '../../ui.js'\nimport useLayout from '../hooks/use-layout.js'\nimport {messageWithPunctuation} from '../utilities.js'\nimport React, {useCallback, useState} from 'react'\nimport {Box, useApp, useInput, Text} from 'ink'\nimport figures from 'figures'\n\nexport interface Props {\n message: string\n onSubmit: (value: string) => void\n defaultValue?: string\n password?: boolean\n validate?: (value: string) => string | undefined\n}\n\nconst TextPrompt: React.FC<Props> = ({message, onSubmit, validate, defaultValue = '', password = false}) => {\n if (password && defaultValue) {\n throw new Error(\"Can't use defaultValue with password\")\n }\n\n const validateAnswer = (value: string): string | undefined => {\n if (validate) {\n return validate(value)\n }\n\n if (value.length === 0) return 'Type an answer to the prompt.'\n\n return undefined\n }\n const {oneThird} = useLayout()\n const [answer, setAnswer] = useState<string>('')\n const answerOrDefault = answer.length > 0 ? answer : defaultValue\n const {exit: unmountInk} = useApp()\n const [submitted, setSubmitted] = useState(false)\n const [error, setError] = useState<string | undefined>(undefined)\n const shouldShowError = submitted && error\n const color = shouldShowError ? 'red' : 'cyan'\n const underline = new Array(oneThird - 3).fill('▔')\n\n useInput(\n useCallback(\n (input, key) => {\n handleCtrlC(input, key)\n\n if (key.return) {\n setSubmitted(true)\n const error = validateAnswer(answerOrDefault)\n setError(error)\n\n if (!error) {\n onSubmit(answerOrDefault)\n unmountInk()\n }\n }\n },\n [answerOrDefault, onSubmit],\n ),\n )\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} width={oneThird}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <Text>{messageWithPunctuation(message)}</Text>\n </Box>\n {submitted && !error ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Box flexGrow={1}>\n <Text color=\"cyan\">{password ? '*'.repeat(answer.length) : answerOrDefault}</Text>\n </Box>\n </Box>\n ) : (\n <Box flexDirection=\"column\">\n <Box>\n <Box marginRight={2}>\n <Text color={color}>{`>`}</Text>\n </Box>\n <Box flexGrow={1}>\n <TextInput\n value={answer}\n onChange={(answer) => {\n setAnswer(answer)\n setSubmitted(false)\n }}\n defaultValue={defaultValue}\n color={color}\n password={password}\n />\n </Box>\n </Box>\n <Box marginLeft={3}>\n <Text color={color}>{underline}</Text>\n </Box>\n {shouldShowError && (\n <Box marginLeft={3}>\n <Text color={color}>{error}</Text>\n </Box>\n )}\n </Box>\n )}\n </Box>\n )\n}\n\nexport {TextPrompt}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { TextPrompt } from './TextPrompt.js';
2
- import { getLastFrameAfterUnmount, sendInput, waitForInputsToBeReady } from '../../../../testing/ui.js';
2
+ import { getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady } from '../../../../testing/ui.js';
3
3
  import { unstyled } from '../../../../output.js';
4
4
  import { render } from 'ink-testing-library';
5
5
  import React from 'react';
@@ -7,44 +7,70 @@ import { describe, expect, test, vi } from 'vitest';
7
7
  const ENTER = '\r';
8
8
  describe('TextPrompt', () => {
9
9
  test('default state', () => {
10
- const { lastFrame } = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question", placeholder: "Placeholder" }));
10
+ const { lastFrame } = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question", defaultValue: "Placeholder" }));
11
11
  expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
12
- "? Test question
12
+ "? Test question:
13
13
  > Placeholder
14
14
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
15
15
  "
16
16
  `);
17
17
  });
18
- test('validation error', async () => {
18
+ test('default validation error', async () => {
19
19
  const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question" }));
20
20
  await waitForInputsToBeReady();
21
- await sendInput(renderInstance, ENTER);
21
+ await sendInputAndWaitForChange(renderInstance, ENTER);
22
22
  // testing with styles because the color changes to red
23
23
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
24
- "? Test question
24
+ "? Test question:
25
25
  >  
26
26
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
27
- Please enter a value
27
+ Type an answer to the prompt.
28
28
  "
29
29
  `);
30
- await sendInput(renderInstance, 'A');
30
+ await sendInputAndWaitForChange(renderInstance, 'A');
31
31
  // color changes back to valid color
32
32
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
33
- "? Test question
33
+ "? Test question:
34
34
  > A 
35
35
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
36
36
  "
37
37
  `);
38
38
  });
39
+ test('custom validation error', async () => {
40
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question", validate: (value) => (value.includes('shopify') ? "App Name can't include the word shopify" : undefined) }));
41
+ await waitForInputsToBeReady();
42
+ await sendInputAndWaitForChange(renderInstance, 'this-test-includes-shopify');
43
+ await sendInputAndWaitForChange(renderInstance, ENTER);
44
+ // testing with styles because the color changes to red
45
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
46
+ "? Test question:
47
+ > this-test-includes-shopify 
48
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
49
+ App Name can't include the word shopify
50
+ "
51
+ `);
52
+ });
39
53
  test('submitting the value', async () => {
40
54
  const onSubmit = vi.fn();
41
55
  const renderInstance = render(React.createElement(TextPrompt, { onSubmit: onSubmit, message: "Test question" }));
42
56
  await waitForInputsToBeReady();
43
- await sendInput(renderInstance, 'A');
44
- await sendInput(renderInstance, ENTER);
57
+ await sendInputAndWaitForChange(renderInstance, 'A');
58
+ await sendInputAndWaitForChange(renderInstance, ENTER);
59
+ expect(onSubmit).toHaveBeenCalledWith('A');
60
+ expect(unstyled(getLastFrameAfterUnmount(renderInstance))).toMatchInlineSnapshot(`
61
+ "? Test question:
62
+ ✔ A
63
+ "
64
+ `);
65
+ });
66
+ test('submitting the default value', async () => {
67
+ const onSubmit = vi.fn();
68
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: onSubmit, message: "Test question", defaultValue: "A" }));
69
+ await waitForInputsToBeReady();
70
+ await sendInputAndWaitForChange(renderInstance, ENTER);
45
71
  expect(onSubmit).toHaveBeenCalledWith('A');
46
72
  expect(unstyled(getLastFrameAfterUnmount(renderInstance))).toMatchInlineSnapshot(`
47
- "? Test question
73
+ "? Test question:
48
74
  ✔ A
49
75
  "
50
76
  `);
@@ -54,15 +80,45 @@ describe('TextPrompt', () => {
54
80
  // spaces before the question, we only have 77 characters to work with
55
81
  const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question" }));
56
82
  await waitForInputsToBeReady();
57
- await sendInput(renderInstance, 'A'.repeat(77));
58
- await sendInput(renderInstance, 'B'.repeat(6));
83
+ await sendInputAndWaitForChange(renderInstance, 'A'.repeat(77));
84
+ await sendInputAndWaitForChange(renderInstance, 'B'.repeat(6));
59
85
  expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
60
- "? Test question
86
+ "? Test question:
61
87
  > AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
62
88
  BBBBBB 
63
89
  ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
64
90
  "
65
91
  `);
66
92
  });
93
+ test("masking the input if it's a password", async () => {
94
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question", password: true }));
95
+ await waitForInputsToBeReady();
96
+ await sendInputAndWaitForChange(renderInstance, 'ABC');
97
+ expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`
98
+ "? Test question:
99
+ > *** 
100
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
101
+ "
102
+ `);
103
+ await sendInputAndWaitForChange(renderInstance, ENTER);
104
+ expect(unstyled(getLastFrameAfterUnmount(renderInstance))).toMatchInlineSnapshot(`
105
+ "? Test question:
106
+ ✔ ***
107
+ "
108
+ `);
109
+ });
110
+ test("doesn't append a colon to the message if it ends with a question mark", async () => {
111
+ const { lastFrame } = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question?" }));
112
+ expect(lastFrame()).toMatchInlineSnapshot(`
113
+ "? Test question?
114
+ >  
115
+ ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
116
+ "
117
+ `);
118
+ });
119
+ test("doesn't allow to pass defaultValue and password at the same time", async () => {
120
+ const renderInstance = render(React.createElement(TextPrompt, { onSubmit: () => { }, message: "Test question", password: true, defaultValue: "A" }));
121
+ expect(unstyled(getLastFrameAfterUnmount(renderInstance))).toContain("ERROR Can't use defaultValue with password");
122
+ });
67
123
  });
68
124
  //# sourceMappingURL=TextPrompt.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextPrompt} from './TextPrompt.js'\nimport {getLastFrameAfterUnmount, sendInput, waitForInputsToBeReady} from '../../../../testing/ui.js'\nimport {unstyled} from '../../../../output.js'\nimport {render} from 'ink-testing-library'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\n\nconst ENTER = '\\r'\n\ndescribe('TextPrompt', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" placeholder=\"Placeholder\" />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question\n > Placeholder\n ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n \"\n `)\n })\n\n test('validation error', async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question\n \u001b[31m>\u001b[39m \u001b[31m\u001b[7m \u001b[27m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mPlease enter a value\u001b[39m\n \"\n `)\n await sendInput(renderInstance, 'A')\n // color changes back to valid color\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question\n \u001b[36m>\u001b[39m \u001b[36mA\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test('submitting the value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'A')\n await sendInput(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question\n ✔ A\n \"\n `)\n })\n\n test('text wrapping', async () => {\n // component width is 80 characters wide in tests but because of the question mark and\n // spaces before the question, we only have 77 characters to work with\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInput(renderInstance, 'A'.repeat(77))\n await sendInput(renderInstance, 'B'.repeat(6))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question\n \u001b[36m>\u001b[39m \u001b[36mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[39m\n \u001b[36mBBBBBB\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n})\n"]}
1
+ {"version":3,"file":"TextPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/TextPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextPrompt} from './TextPrompt.js'\nimport {getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady} from '../../../../testing/ui.js'\nimport {unstyled} from '../../../../output.js'\nimport {render} from 'ink-testing-library'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\n\nconst ENTER = '\\r'\n\ndescribe('TextPrompt', () => {\n test('default state', () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" defaultValue=\"Placeholder\" />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question:\n > Placeholder\n ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\n \"\n `)\n })\n\n test('default validation error', async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[31m>\u001b[39m \u001b[31m\u001b[7m \u001b[27m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mType an answer to the prompt.\u001b[39m\n \"\n `)\n await sendInputAndWaitForChange(renderInstance, 'A')\n // color changes back to valid color\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36mA\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test('custom validation error', async () => {\n const renderInstance = render(\n <TextPrompt\n onSubmit={() => {}}\n message=\"Test question\"\n validate={(value) => (value.includes('shopify') ? \"App Name can't include the word shopify\" : undefined)}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'this-test-includes-shopify')\n await sendInputAndWaitForChange(renderInstance, ENTER)\n // testing with styles because the color changes to red\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[31m>\u001b[39m \u001b[31mthis-test-includes-shopify\u001b[7m \u001b[27m\u001b[39m\n \u001b[31m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \u001b[31mApp Name can't include the word shopify\u001b[39m\n \"\n `)\n })\n\n test('submitting the value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A')\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('submitting the default value', async () => {\n const onSubmit = vi.fn()\n const renderInstance = render(<TextPrompt onSubmit={onSubmit} message=\"Test question\" defaultValue=\"A\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(onSubmit).toHaveBeenCalledWith('A')\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ A\n \"\n `)\n })\n\n test('text wrapping', async () => {\n // component width is 80 characters wide in tests but because of the question mark and\n // spaces before the question, we only have 77 characters to work with\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'A'.repeat(77))\n await sendInputAndWaitForChange(renderInstance, 'B'.repeat(6))\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[39m\n \u001b[36mBBBBBB\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test(\"masking the input if it's a password\", async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" password />)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'ABC')\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Test question:\n \u001b[36m>\u001b[39m \u001b[36m***\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n\n await sendInputAndWaitForChange(renderInstance, ENTER)\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toMatchInlineSnapshot(`\n \"? Test question:\n ✔ ***\n \"\n `)\n })\n\n test(\"doesn't append a colon to the message if it ends with a question mark\", async () => {\n const {lastFrame} = render(<TextPrompt onSubmit={() => {}} message=\"Test question?\" />)\n\n expect(lastFrame()!).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m>\u001b[39m \u001b[36m\u001b[7m \u001b[27m\u001b[39m\n \u001b[36m▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔\u001b[39m\n \"\n `)\n })\n\n test(\"doesn't allow to pass defaultValue and password at the same time\", async () => {\n const renderInstance = render(<TextPrompt onSubmit={() => {}} message=\"Test question\" password defaultValue=\"A\" />)\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toContain(\"ERROR Can't use defaultValue with password\")\n })\n})\n"]}
@@ -22,12 +22,15 @@ interface FilePathToken {
22
22
  }
23
23
  interface ListToken {
24
24
  list: {
25
+ title?: string;
25
26
  items: TokenItem[];
26
27
  ordered?: boolean;
27
28
  };
28
29
  }
29
30
  declare type Token = string | CommandToken | LinkToken | CharToken | UserInputToken | SubduedToken | FilePathToken | ListToken;
30
31
  export declare type TokenItem = Token | Token[];
32
+ export declare function tokenItemToString(token: TokenItem): string;
33
+ export declare function appendToTokenItem(token: TokenItem, suffix: string): TokenItem;
31
34
  interface Props {
32
35
  item: TokenItem;
33
36
  }
@@ -12,6 +12,38 @@ function tokenToBlock(token) {
12
12
  value: token,
13
13
  };
14
14
  }
15
+ export function tokenItemToString(token) {
16
+ if (typeof token === 'string') {
17
+ return token;
18
+ }
19
+ else if ('command' in token) {
20
+ return token.command;
21
+ }
22
+ else if ('link' in token) {
23
+ return token.link.label || token.link.url;
24
+ }
25
+ else if ('char' in token) {
26
+ return token.char;
27
+ }
28
+ else if ('userInput' in token) {
29
+ return token.userInput;
30
+ }
31
+ else if ('subdued' in token) {
32
+ return token.subdued;
33
+ }
34
+ else if ('filePath' in token) {
35
+ return token.filePath;
36
+ }
37
+ else if ('list' in token) {
38
+ return token.list.items.map(tokenItemToString).join(' ');
39
+ }
40
+ else {
41
+ return token.map(tokenItemToString).join(' ');
42
+ }
43
+ }
44
+ export function appendToTokenItem(token, suffix) {
45
+ return Array.isArray(token) ? [...token, suffix] : [token, suffix];
46
+ }
15
47
  function splitByDisplayType(acc, item) {
16
48
  if (item.display === 'block') {
17
49
  acc.push([item]);
@@ -65,7 +97,7 @@ const TokenizedText = ({ item }) => {
65
97
  React.createElement(TokenizedText, { item: item.value }))))));
66
98
  }
67
99
  else {
68
- return React.createElement(List, { key: groupIndex, items: items[0].value.list.items });
100
+ return React.createElement(List, { key: groupIndex, ...items[0].value.list });
69
101
  }
70
102
  })));
71
103
  }