cdd-cli 4.0.0 → 4.1.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 (130) hide show
  1. package/README.es.md +29 -5
  2. package/README.md +29 -5
  3. package/dist/components/UsageMenu.js +1 -1
  4. package/dist/helpers/appState.js +26 -0
  5. package/dist/helpers/dockerService/serviceComponents/containerExec.js +211 -0
  6. package/dist/hooks/creation/useContainerCreation.js +25 -6
  7. package/dist/hooks/useContainerCommandRouter.js +12 -2
  8. package/dist/hooks/useControls.js +5 -0
  9. package/dist/hooks/useShellMode.js +101 -0
  10. package/dist/index.js +3 -1
  11. package/package.json +4 -1
  12. package/.atl/skill-registry.md +0 -30
  13. package/.editorconfig +0 -12
  14. package/.eslintrc.json +0 -30
  15. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -30
  16. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  17. package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +0 -14
  18. package/.github/workflows/ci.yml +0 -32
  19. package/.github/workflows/docs.yml +0 -42
  20. package/.prettierignore +0 -4
  21. package/.prettierrc +0 -8
  22. package/.~lock.ROADMAP.md# +0 -1
  23. package/CHANGELOG.md +0 -216
  24. package/CODE_OF_CONDUCT.md +0 -12
  25. package/CONTRIBUTING.md +0 -35
  26. package/ROADMAP.md +0 -647
  27. package/SECURITY.md +0 -5
  28. package/__mocks__/ink.cjs +0 -17
  29. package/_config.yml +0 -13
  30. package/babel.config.cjs +0 -23
  31. package/coverage/clover.xml +0 -575
  32. package/coverage/coverage-final.json +0 -17
  33. package/coverage/lcov-report/ContainerCreationPrompt.jsx.html +0 -400
  34. package/coverage/lcov-report/ControlsHUD.jsx.html +0 -256
  35. package/coverage/lcov-report/base.css +0 -224
  36. package/coverage/lcov-report/block-navigation.js +0 -87
  37. package/coverage/lcov-report/components/ContainerCreationPrompt.jsx.html +0 -394
  38. package/coverage/lcov-report/components/PromptField.jsx.html +0 -229
  39. package/coverage/lcov-report/components/SuggestionPanel.jsx.html +0 -268
  40. package/coverage/lcov-report/components/index.html +0 -146
  41. package/coverage/lcov-report/favicon.png +0 -0
  42. package/coverage/lcov-report/helpers/constants.js.html +0 -352
  43. package/coverage/lcov-report/helpers/containerOptionsBuilder.js.html +0 -244
  44. package/coverage/lcov-report/helpers/dockerHubService.js.html +0 -310
  45. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerActions.js.html +0 -931
  46. package/coverage/lcov-report/helpers/dockerService/serviceComponents/containerList.js.html +0 -307
  47. package/coverage/lcov-report/helpers/dockerService/serviceComponents/index.html +0 -131
  48. package/coverage/lcov-report/helpers/imageNameUtils.js.html +0 -181
  49. package/coverage/lcov-report/helpers/index.html +0 -206
  50. package/coverage/lcov-report/helpers/logger.js.html +0 -424
  51. package/coverage/lcov-report/helpers/safeCall.js.html +0 -139
  52. package/coverage/lcov-report/helpers/validationHelpers.js.html +0 -346
  53. package/coverage/lcov-report/hooks/creation/index.html +0 -146
  54. package/coverage/lcov-report/hooks/creation/useContainerActions.js.html +0 -313
  55. package/coverage/lcov-report/hooks/creation/useContainerCreation.js.html +0 -982
  56. package/coverage/lcov-report/hooks/creation/useLogsViewer.js.html +0 -238
  57. package/coverage/lcov-report/hooks/debug/index.html +0 -116
  58. package/coverage/lcov-report/hooks/debug/useDebugLogs.js.html +0 -187
  59. package/coverage/lcov-report/hooks/index.html +0 -161
  60. package/coverage/lcov-report/hooks/navigation/index.html +0 -116
  61. package/coverage/lcov-report/hooks/navigation/useContainerSelection.js.html +0 -169
  62. package/coverage/lcov-report/hooks/useContainerCommandRouter.js.html +0 -454
  63. package/coverage/lcov-report/hooks/useControls.js.html +0 -868
  64. package/coverage/lcov-report/hooks/useEraseConfirmation.js.html +0 -202
  65. package/coverage/lcov-report/hooks/useExitHandler.js.html +0 -292
  66. package/coverage/lcov-report/index.html +0 -176
  67. package/coverage/lcov-report/prettify.css +0 -1
  68. package/coverage/lcov-report/prettify.js +0 -2
  69. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  70. package/coverage/lcov-report/sorter.js +0 -210
  71. package/coverage/lcov.info +0 -1163
  72. package/fix-imports.cjs +0 -54
  73. package/jest.config.cjs +0 -14
  74. package/src/App.jsx +0 -102
  75. package/src/components/ContainerCreationPrompt.jsx +0 -107
  76. package/src/components/ContainerList.jsx +0 -32
  77. package/src/components/ContainerRow.jsx +0 -84
  78. package/src/components/ContainerSection.jsx +0 -24
  79. package/src/components/ControlsHUD.jsx +0 -66
  80. package/src/components/Footer.jsx +0 -15
  81. package/src/components/Header.jsx +0 -41
  82. package/src/components/LogViewer.jsx +0 -36
  83. package/src/components/MessageFeedback.jsx +0 -25
  84. package/src/components/PromptField.jsx +0 -48
  85. package/src/components/StatsBar.jsx +0 -70
  86. package/src/components/SuggestionPanel.jsx +0 -61
  87. package/src/components/UsageMenu.jsx +0 -52
  88. package/src/helpers/appInfo.js +0 -23
  89. package/src/helpers/constants.js +0 -89
  90. package/src/helpers/containerOptionsBuilder.js +0 -53
  91. package/src/helpers/dockerHubService.js +0 -75
  92. package/src/helpers/dockerService/dockerService.js +0 -28
  93. package/src/helpers/dockerService/serviceComponents/containerActions.js +0 -282
  94. package/src/helpers/dockerService/serviceComponents/containerList.js +0 -74
  95. package/src/helpers/dockerService/serviceComponents/containerLogs.js +0 -60
  96. package/src/helpers/dockerService/serviceComponents/containerStats.js +0 -69
  97. package/src/helpers/dockerService/serviceComponents/imageUtils.js +0 -32
  98. package/src/helpers/exitWithMessage.js +0 -32
  99. package/src/helpers/imageNameUtils.js +0 -32
  100. package/src/helpers/logger.js +0 -112
  101. package/src/helpers/safeCall.js +0 -18
  102. package/src/helpers/validationHelpers.js +0 -87
  103. package/src/hooks/creation/useContainerActions.js +0 -89
  104. package/src/hooks/creation/useContainerCreation.js +0 -377
  105. package/src/hooks/creation/useLogsViewer.js +0 -51
  106. package/src/hooks/debug/useDebugLogs.js +0 -34
  107. package/src/hooks/navigation/useContainerSelection.js +0 -28
  108. package/src/hooks/useContainerCommandRouter.js +0 -123
  109. package/src/hooks/useContainerStats.js +0 -44
  110. package/src/hooks/useContainers.js +0 -26
  111. package/src/hooks/useControls.js +0 -263
  112. package/src/hooks/useEraseConfirmation.js +0 -39
  113. package/src/hooks/useExitHandler.js +0 -69
  114. package/src/index.js +0 -20
  115. package/test/ContainerCreationPrompt.dom.test.js +0 -313
  116. package/test/ControlsHUD.dom.test.js +0 -125
  117. package/test/SuggestionPanel.dom.test.js +0 -83
  118. package/test/constants.test.js +0 -41
  119. package/test/containerActions.test.js +0 -229
  120. package/test/containerList.test.js +0 -79
  121. package/test/containerOptionsBuilder.test.js +0 -36
  122. package/test/dockerHubService.test.js +0 -213
  123. package/test/jest.setup.js +0 -7
  124. package/test/logger.test.js +0 -58
  125. package/test/resolveImageTag.test.js +0 -26
  126. package/test/safeCall.test.js +0 -25
  127. package/test/useContainerCreation.dom.test.js +0 -567
  128. package/test/useContainerCreationHub.dom.test.js +0 -184
  129. package/test/useControls.dom.test.js +0 -519
  130. package/test/validationHelpers.test.js +0 -111
@@ -1,313 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
- import React from 'react';
5
- import { render } from '@testing-library/react';
6
- import { ContainerCreationPrompt } from '../src/components/ContainerCreationPrompt.jsx';
7
-
8
- describe('ContainerCreationPrompt — SuggestionPanel wiring', () => {
9
- test('does NOT render SuggestionPanel when step > 0', () => {
10
- const { queryByText } = render(
11
- <ContainerCreationPrompt
12
- step={1}
13
- imageName="nginx"
14
- containerName=""
15
- portInput=""
16
- envInput=""
17
- message=""
18
- messageColor="yellow"
19
- suggestions={['nginx', 'node']}
20
- selectedSuggestionIndex={-1}
21
- visibleOffset={0}
22
- />
23
- );
24
- // On step 1, the imageName field is not active — no suggestion panel
25
- expect(queryByText(/^nginx$/)).toBeNull();
26
- });
27
-
28
- test('does NOT render SuggestionPanel on step 0 when suggestions is empty', () => {
29
- const { queryByTestId } = render(
30
- <ContainerCreationPrompt
31
- step={0}
32
- imageName="ng"
33
- containerName=""
34
- portInput=""
35
- envInput=""
36
- message=""
37
- messageColor="yellow"
38
- suggestions={[]}
39
- selectedSuggestionIndex={-1}
40
- visibleOffset={0}
41
- />
42
- );
43
- // No suggestion panel should render when suggestions array is empty
44
- expect(queryByTestId('suggestion-panel')).toBeNull();
45
- });
46
-
47
- test('renders SuggestionPanel items on step 0 when suggestions exist', () => {
48
- const { getAllByText, getByText } = render(
49
- <ContainerCreationPrompt
50
- step={0}
51
- imageName="ng"
52
- containerName=""
53
- portInput=""
54
- envInput=""
55
- message=""
56
- messageColor="yellow"
57
- suggestions={['nginx', 'golang']}
58
- selectedSuggestionIndex={-1}
59
- visibleOffset={0}
60
- />
61
- );
62
- // golang is not in any label — appears only in suggestion panel
63
- expect(getByText(/golang/)).toBeTruthy();
64
- // nginx appears in suggestion panel (may also appear in label text, but it does appear)
65
- const nginxEls = getAllByText(/nginx/);
66
- expect(nginxEls.length).toBeGreaterThan(0);
67
- });
68
-
69
- // FR8 — hubResults wiring
70
- test('when hubResults is provided, activeItems = hubResults (replaces suggestions)', () => {
71
- const { getByText, queryByText } = render(
72
- <ContainerCreationPrompt
73
- step={0}
74
- imageName="ng"
75
- containerName=""
76
- portInput=""
77
- envInput=""
78
- message=""
79
- messageColor="yellow"
80
- suggestions={['nginx', 'golang']}
81
- hubResults={['hub-nginx', 'hub-node']}
82
- selectedSuggestionIndex={-1}
83
- visibleOffset={0}
84
- />
85
- );
86
- expect(getByText(/hub-nginx/)).toBeTruthy();
87
- expect(queryByText(/golang/)).toBeNull();
88
- });
89
-
90
- test('when hubResults is null, falls back to suggestions', () => {
91
- const { getByText } = render(
92
- <ContainerCreationPrompt
93
- step={0}
94
- imageName="ng"
95
- containerName=""
96
- portInput=""
97
- envInput=""
98
- message=""
99
- messageColor="yellow"
100
- suggestions={['nginx', 'golang']}
101
- hubResults={null}
102
- selectedSuggestionIndex={-1}
103
- visibleOffset={0}
104
- />
105
- );
106
- expect(getByText(/golang/)).toBeTruthy();
107
- });
108
-
109
- test('when step===0 and isSearchingHub=true, SuggestionPanel is shown even if activeItems is empty', () => {
110
- const { getByText } = render(
111
- <ContainerCreationPrompt
112
- step={0}
113
- imageName="ng"
114
- containerName=""
115
- portInput=""
116
- envInput=""
117
- message=""
118
- messageColor="yellow"
119
- suggestions={[]}
120
- hubResults={null}
121
- isSearchingHub={true}
122
- selectedSuggestionIndex={-1}
123
- visibleOffset={0}
124
- />
125
- );
126
- expect(getByText(/searching Docker Hub/)).toBeTruthy();
127
- });
128
-
129
- test('when step===0 and isSearchingHub=false and activeItems is empty, SuggestionPanel is NOT shown', () => {
130
- const { queryByTestId } = render(
131
- <ContainerCreationPrompt
132
- step={0}
133
- imageName="ng"
134
- containerName=""
135
- portInput=""
136
- envInput=""
137
- message=""
138
- messageColor="yellow"
139
- suggestions={[]}
140
- hubResults={null}
141
- isSearchingHub={false}
142
- selectedSuggestionIndex={-1}
143
- visibleOffset={0}
144
- />
145
- );
146
- expect(queryByTestId('suggestion-panel')).toBeNull();
147
- });
148
-
149
- test('isLoading prop is passed down to SuggestionPanel when isSearchingHub=true', () => {
150
- const { getByText } = render(
151
- <ContainerCreationPrompt
152
- step={0}
153
- imageName="ng"
154
- containerName=""
155
- portInput=""
156
- envInput=""
157
- message=""
158
- messageColor="yellow"
159
- suggestions={['nginx']}
160
- hubResults={null}
161
- isSearchingHub={true}
162
- selectedSuggestionIndex={-1}
163
- visibleOffset={0}
164
- />
165
- );
166
- expect(getByText(/searching Docker Hub/)).toBeTruthy();
167
- });
168
- });
169
-
170
- describe('ContainerCreationPrompt — ControlsHUD wiring', () => {
171
- test('step=0, no suggestions: renders ControlsHUD with [Tab] and Search Hub', () => {
172
- const { getByText } = render(
173
- <ContainerCreationPrompt
174
- step={0}
175
- imageName=""
176
- containerName=""
177
- portInput=""
178
- envInput=""
179
- message=""
180
- messageColor="yellow"
181
- suggestions={[]}
182
- hubResults={null}
183
- isSearchingHub={false}
184
- selectedSuggestionIndex={-1}
185
- visibleOffset={0}
186
- />
187
- );
188
- expect(getByText('[Tab]')).toBeTruthy();
189
- expect(getByText(/Search Hub/)).toBeTruthy();
190
- });
191
-
192
- test('does NOT render old static text "Press Enter to continue, Escape to cancel"', () => {
193
- const { queryByText } = render(
194
- <ContainerCreationPrompt
195
- step={0}
196
- imageName=""
197
- containerName=""
198
- portInput=""
199
- envInput=""
200
- message=""
201
- messageColor="yellow"
202
- suggestions={[]}
203
- hubResults={null}
204
- isSearchingHub={false}
205
- selectedSuggestionIndex={-1}
206
- visibleOffset={0}
207
- />
208
- );
209
- expect(queryByText(/Press Enter to continue, Escape to cancel/)).toBeNull();
210
- });
211
-
212
- test('step=0, hasSuggestions=true: renders [↑↓] not [Tab]', () => {
213
- const { getByText, queryByText } = render(
214
- <ContainerCreationPrompt
215
- step={0}
216
- imageName="ng"
217
- containerName=""
218
- portInput=""
219
- envInput=""
220
- message=""
221
- messageColor="yellow"
222
- suggestions={['nginx', 'golang']}
223
- hubResults={null}
224
- isSearchingHub={false}
225
- selectedSuggestionIndex={-1}
226
- visibleOffset={0}
227
- />
228
- );
229
- expect(getByText('[↑↓]')).toBeTruthy();
230
- expect(queryByText('[Tab]')).toBeNull();
231
- });
232
-
233
- test('step=0, isSearchingHub=true, no suggestions: ControlsHUD does NOT render [Tab] or Navigate, DOES render [Enter] and [Esc]', () => {
234
- const { queryByText, getByText } = render(
235
- <ContainerCreationPrompt
236
- step={0}
237
- imageName="ng"
238
- containerName=""
239
- portInput=""
240
- envInput=""
241
- message=""
242
- messageColor="yellow"
243
- suggestions={[]}
244
- hubResults={null}
245
- isSearchingHub={true}
246
- selectedSuggestionIndex={-1}
247
- visibleOffset={0}
248
- />
249
- );
250
- expect(queryByText('[Tab]')).toBeNull();
251
- expect(queryByText(/Navigate/)).toBeNull();
252
- expect(getByText('[Enter]')).toBeTruthy();
253
- expect(getByText('[Esc]')).toBeTruthy();
254
- });
255
-
256
- test('step=1: renders [Enter] and [Esc]', () => {
257
- const { getByText } = render(
258
- <ContainerCreationPrompt
259
- step={1}
260
- imageName="nginx"
261
- containerName=""
262
- portInput=""
263
- envInput=""
264
- message=""
265
- messageColor="yellow"
266
- suggestions={[]}
267
- selectedSuggestionIndex={-1}
268
- visibleOffset={0}
269
- />
270
- );
271
- expect(getByText('[Enter]')).toBeTruthy();
272
- expect(getByText('[Esc]')).toBeTruthy();
273
- });
274
-
275
- test('step=3, hasSuggestedEnv=true: renders [Tab] in HUD', () => {
276
- const { getByText } = render(
277
- <ContainerCreationPrompt
278
- step={3}
279
- imageName="postgres:17-alpine"
280
- containerName=""
281
- portInput=""
282
- envInput=""
283
- message=""
284
- messageColor="yellow"
285
- suggestions={[]}
286
- selectedSuggestionIndex={-1}
287
- visibleOffset={0}
288
- hasSuggestedEnv={true}
289
- />
290
- );
291
- expect(getByText('[Tab]')).toBeTruthy();
292
- expect(getByText(/Insert next env/)).toBeTruthy();
293
- });
294
-
295
- test('step=3, hasSuggestedEnv=false: does NOT render [Tab] in HUD', () => {
296
- const { queryByText } = render(
297
- <ContainerCreationPrompt
298
- step={3}
299
- imageName="nginx:1.27-alpine"
300
- containerName=""
301
- portInput=""
302
- envInput=""
303
- message=""
304
- messageColor="yellow"
305
- suggestions={[]}
306
- selectedSuggestionIndex={-1}
307
- visibleOffset={0}
308
- hasSuggestedEnv={false}
309
- />
310
- );
311
- expect(queryByText('[Tab]')).toBeNull();
312
- });
313
- });
@@ -1,125 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
- import React from 'react';
5
- import { render } from '@testing-library/react';
6
- import { getHints, ControlsHUD } from '../src/components/ControlsHUD.jsx';
7
-
8
- describe('getHints — pure function', () => {
9
- test('step=0, no suggestions, not searching: includes Tab=Search Hub', () => {
10
- const hints = getHints(0, false, false);
11
- const keys = hints.map(h => h.key);
12
- expect(keys).toContain('Tab');
13
- const tabHint = hints.find(h => h.key === 'Tab');
14
- expect(tabHint.label).toBe('Search Hub');
15
- });
16
-
17
- test('step=0, hasSuggestions=true: includes ↑↓ Navigate, does NOT include Tab', () => {
18
- const hints = getHints(0, true, false);
19
- const keys = hints.map(h => h.key);
20
- expect(keys).toContain('↑↓');
21
- expect(keys).not.toContain('Tab');
22
- const navHint = hints.find(h => h.key === '↑↓');
23
- expect(navHint.label).toBe('Navigate');
24
- });
25
-
26
- test('step=0, isSearchingHub=true: does NOT include Tab, includes Enter and Esc', () => {
27
- const hints = getHints(0, false, true);
28
- const keys = hints.map(h => h.key);
29
- expect(keys).not.toContain('Tab');
30
- expect(keys).toContain('Enter');
31
- expect(keys).toContain('Esc');
32
- });
33
-
34
- test('step=1: includes Enter=Continue and Esc=Cancel, no Tab, no ↑↓', () => {
35
- const hints = getHints(1, false, false);
36
- const keys = hints.map(h => h.key);
37
- expect(keys).toContain('Enter');
38
- expect(keys).toContain('Esc');
39
- expect(keys).not.toContain('Tab');
40
- expect(keys).not.toContain('↑↓');
41
- const enterHint = hints.find(h => h.key === 'Enter');
42
- expect(enterHint.label).toBe('Continue');
43
- });
44
-
45
- test('step=2: same as step=1', () => {
46
- const hints = getHints(2, false, false);
47
- const keys = hints.map(h => h.key);
48
- expect(keys).toContain('Enter');
49
- expect(keys).toContain('Esc');
50
- expect(keys).not.toContain('Tab');
51
- expect(keys).not.toContain('↑↓');
52
- });
53
-
54
- test('step=3: same as step=1', () => {
55
- const hints = getHints(3, false, false);
56
- const keys = hints.map(h => h.key);
57
- expect(keys).toContain('Enter');
58
- expect(keys).toContain('Esc');
59
- expect(keys).not.toContain('Tab');
60
- expect(keys).not.toContain('↑↓');
61
- });
62
-
63
- test('getHints(3, false, false, true) includes Tab=Insert next env', () => {
64
- const hints = getHints(3, false, false, true);
65
- const keys = hints.map(h => h.key);
66
- expect(keys).toContain('Tab');
67
- const tabHint = hints.find(h => h.key === 'Tab');
68
- expect(tabHint.label).toBe('Insert next env');
69
- });
70
-
71
- test('getHints(3, false, false, false) does NOT include Tab hint', () => {
72
- const hints = getHints(3, false, false, false);
73
- const keys = hints.map(h => h.key);
74
- expect(keys).not.toContain('Tab');
75
- });
76
-
77
- test('getHints(3, false, false, true) also includes Enter and Esc', () => {
78
- const hints = getHints(3, false, false, true);
79
- const keys = hints.map(h => h.key);
80
- expect(keys).toContain('Enter');
81
- expect(keys).toContain('Esc');
82
- });
83
- });
84
-
85
- describe('ControlsHUD — component rendering', () => {
86
- test('step=0, no suggestions: renders [Tab] and "Search Hub" text', () => {
87
- const { getByText } = render(
88
- <ControlsHUD step={0} hasSuggestions={false} isSearchingHub={false} />
89
- );
90
- expect(getByText('[Tab]')).toBeTruthy();
91
- expect(getByText(/Search Hub/)).toBeTruthy();
92
- });
93
-
94
- test('step=0, hasSuggestions=true: renders [↑↓], does NOT render [Tab]', () => {
95
- const { getByText, queryByText } = render(
96
- <ControlsHUD step={0} hasSuggestions={true} isSearchingHub={false} />
97
- );
98
- expect(getByText('[↑↓]')).toBeTruthy();
99
- expect(queryByText('[Tab]')).toBeNull();
100
- });
101
-
102
- test('step=1: renders [Enter] and [Esc], does NOT render [Tab]', () => {
103
- const { getByText, queryByText } = render(
104
- <ControlsHUD step={1} hasSuggestions={false} isSearchingHub={false} />
105
- );
106
- expect(getByText('[Enter]')).toBeTruthy();
107
- expect(getByText('[Esc]')).toBeTruthy();
108
- expect(queryByText('[Tab]')).toBeNull();
109
- });
110
-
111
- test('step=3, hasSuggestedEnv=true: renders [Tab] and "Insert next env"', () => {
112
- const { getByText } = render(
113
- <ControlsHUD step={3} hasSuggestions={false} isSearchingHub={false} hasSuggestedEnv={true} />
114
- );
115
- expect(getByText('[Tab]')).toBeTruthy();
116
- expect(getByText(/Insert next env/)).toBeTruthy();
117
- });
118
-
119
- test('step=3, hasSuggestedEnv=false: does NOT render [Tab]', () => {
120
- const { queryByText } = render(
121
- <ControlsHUD step={3} hasSuggestions={false} isSearchingHub={false} hasSuggestedEnv={false} />
122
- );
123
- expect(queryByText('[Tab]')).toBeNull();
124
- });
125
- });
@@ -1,83 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
- import React from 'react';
5
- import { render } from '@testing-library/react';
6
- import { SuggestionPanel } from '../src/components/SuggestionPanel.jsx';
7
-
8
- describe('SuggestionPanel', () => {
9
- const items = ['caddy', 'elasticsearch', 'golang', 'httpd', 'kafka', 'mariadb', 'memcached', 'minio'];
10
-
11
- test('renders nothing when items is empty', () => {
12
- const { container } = render(
13
- <SuggestionPanel items={[]} selectedIndex={-1} visibleOffset={0} maxVisible={5} />
14
- );
15
- expect(container.firstChild).toBeNull();
16
- });
17
-
18
- test('renders only maxVisible items from visibleOffset', () => {
19
- const { getByText, queryByText } = render(
20
- <SuggestionPanel items={items} selectedIndex={-1} visibleOffset={0} maxVisible={5} />
21
- );
22
- expect(getByText(/caddy/)).toBeTruthy();
23
- expect(getByText(/elasticsearch/)).toBeTruthy();
24
- expect(getByText(/kafka/)).toBeTruthy();
25
- expect(queryByText(/mariadb/)).toBeNull();
26
- });
27
-
28
- test('respects visibleOffset by skipping early items', () => {
29
- const { queryByText, getByText } = render(
30
- <SuggestionPanel items={items} selectedIndex={-1} visibleOffset={2} maxVisible={3} />
31
- );
32
- expect(getByText(/golang/)).toBeTruthy();
33
- expect(getByText(/httpd/)).toBeTruthy();
34
- expect(getByText(/kafka/)).toBeTruthy();
35
- expect(queryByText(/caddy/)).toBeNull();
36
- });
37
-
38
- test('focused row has › prefix marker', () => {
39
- const { getByText } = render(
40
- <SuggestionPanel items={items} selectedIndex={1} visibleOffset={0} maxVisible={5} />
41
- );
42
- const el = getByText(/› elasticsearch/);
43
- expect(el).toBeTruthy();
44
- });
45
-
46
- test('non-focused rows do NOT have › prefix', () => {
47
- const { getByText } = render(
48
- <SuggestionPanel items={items} selectedIndex={1} visibleOffset={0} maxVisible={5} />
49
- );
50
- const el = getByText(/caddy/);
51
- expect(el.textContent).not.toMatch(/›/);
52
- });
53
-
54
- // FR8 — isLoading prop
55
- test('renders [searching Docker Hub...] row when isLoading=true', () => {
56
- const { getByText } = render(
57
- <SuggestionPanel items={[]} selectedIndex={-1} visibleOffset={0} isLoading={true} />
58
- );
59
- expect(getByText(/searching Docker Hub/)).toBeTruthy();
60
- });
61
-
62
- test('loading row is NOT selectable — no › prefix on loading row', () => {
63
- const { getByText } = render(
64
- <SuggestionPanel items={items} selectedIndex={-1} visibleOffset={0} maxVisible={5} isLoading={true} />
65
- );
66
- const loadingEl = getByText(/searching Docker Hub/);
67
- expect(loadingEl.textContent).not.toMatch(/›/);
68
- });
69
-
70
- test('does NOT render loading row when isLoading=false', () => {
71
- const { queryByText } = render(
72
- <SuggestionPanel items={items} selectedIndex={-1} visibleOffset={0} maxVisible={5} isLoading={false} />
73
- );
74
- expect(queryByText(/searching Docker Hub/)).toBeNull();
75
- });
76
-
77
- test('renders panel even when items is empty but isLoading=true', () => {
78
- const { getByText } = render(
79
- <SuggestionPanel items={[]} selectedIndex={-1} visibleOffset={0} isLoading={true} />
80
- );
81
- expect(getByText(/searching Docker Hub/)).toBeTruthy();
82
- });
83
- });
@@ -1,41 +0,0 @@
1
- /**
2
- * @jest-environment node
3
- */
4
- import { IMAGE_PROFILES } from '../src/helpers/constants.js';
5
-
6
- describe('IMAGE_PROFILES', () => {
7
- test('has ~20 entries', () => {
8
- const count = Object.keys(IMAGE_PROFILES).length;
9
- expect(count).toBeGreaterThanOrEqual(20);
10
- });
11
-
12
- test('every entry has requiredEnv (array), defaultPort (string), and suggestedEnv (array)', () => {
13
- for (const [key, profile] of Object.entries(IMAGE_PROFILES)) {
14
- expect(Array.isArray(profile.requiredEnv)).toBe(true);
15
- expect(typeof profile.defaultPort).toBe('string');
16
- expect(Array.isArray(profile.suggestedEnv)).toBe(true);
17
- }
18
- });
19
-
20
- test('postgres has POSTGRES_PASSWORD in requiredEnv and suggestedEnv hints', () => {
21
- expect(IMAGE_PROFILES.postgres.requiredEnv).toContain('POSTGRES_PASSWORD');
22
- expect(IMAGE_PROFILES.postgres.suggestedEnv.length).toBeGreaterThan(0);
23
- });
24
-
25
- test('nginx has empty requiredEnv, defaultPort 80, and empty suggestedEnv', () => {
26
- expect(IMAGE_PROFILES.nginx.requiredEnv).toEqual([]);
27
- expect(IMAGE_PROFILES.nginx.defaultPort).toBe('80');
28
- expect(IMAGE_PROFILES.nginx.suggestedEnv).toEqual([]);
29
- });
30
-
31
- test('node has suggestedEnv with NODE_ENV hint', () => {
32
- expect(IMAGE_PROFILES.node.suggestedEnv).toContain('NODE_ENV=production');
33
- });
34
-
35
- test('every entry has defaultTag as a non-empty string', () => {
36
- for (const [key, profile] of Object.entries(IMAGE_PROFILES)) {
37
- expect(typeof profile.defaultTag).toBe('string');
38
- expect(profile.defaultTag.length).toBeGreaterThan(0);
39
- }
40
- });
41
- });