@shohojdhara/atomix 0.5.0 → 0.5.2

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 (168) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +230 -83
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +24 -23
  9. package/dist/charts.js +271 -369
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +3 -2
  15. package/dist/core.js +342 -382
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +4 -6
  18. package/dist/forms.js +233 -334
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +11 -2
  21. package/dist/heavy.js +406 -445
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +109 -65
  24. package/dist/index.esm.js +654 -748
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +621 -717
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +24 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +1 -133
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  55. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
  56. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  57. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
  58. package/src/components/AtomixGlass/glass-utils.ts +51 -1
  59. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  60. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  61. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  62. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  63. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  64. package/src/components/AtomixGlass/stories/types.ts +3 -3
  65. package/src/components/Button/Button.tsx +114 -57
  66. package/src/components/Callout/Callout.tsx +4 -4
  67. package/src/components/Chart/ChartRenderer.tsx +1 -1
  68. package/src/components/Chart/DonutChart.tsx +11 -8
  69. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  70. package/src/components/Form/Select.tsx +4 -4
  71. package/src/components/List/List.tsx +4 -4
  72. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  73. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  74. package/src/components/ProductReview/ProductReview.tsx +4 -2
  75. package/src/components/Rating/Rating.tsx +4 -2
  76. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  77. package/src/components/Steps/Steps.tsx +1 -1
  78. package/src/components/Tabs/Tabs.tsx +5 -5
  79. package/src/components/Testimonial/Testimonial.tsx +4 -2
  80. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  81. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  82. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  83. package/src/layouts/CssGrid/index.ts +8 -0
  84. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  85. package/src/layouts/CssGrid/scripts/index.js +43 -0
  86. package/src/layouts/Grid/scripts/Container.js +139 -0
  87. package/src/layouts/Grid/scripts/Grid.js +184 -0
  88. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  89. package/src/layouts/Grid/scripts/Row.js +154 -0
  90. package/src/layouts/Grid/scripts/index.js +48 -0
  91. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  92. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  93. package/src/lib/composables/useAccordion.ts +5 -5
  94. package/src/lib/composables/useAtomixGlass.ts +111 -74
  95. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  96. package/src/lib/composables/useBarChart.ts +2 -2
  97. package/src/lib/composables/useChart.ts +3 -2
  98. package/src/lib/composables/useChartToolbar.ts +48 -66
  99. package/src/lib/composables/useDataTable.ts +1 -1
  100. package/src/lib/composables/useDatePicker.ts +2 -2
  101. package/src/lib/composables/useEdgePanel.ts +45 -54
  102. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  103. package/src/lib/composables/usePhotoViewer.ts +2 -3
  104. package/src/lib/composables/usePieChart.ts +1 -1
  105. package/src/lib/composables/usePopover.ts +151 -139
  106. package/src/lib/composables/useSideMenu.ts +28 -41
  107. package/src/lib/composables/useSlider.ts +2 -6
  108. package/src/lib/composables/useTooltip.ts +2 -2
  109. package/src/lib/config/index.ts +39 -0
  110. package/src/lib/constants/components.ts +1 -0
  111. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  112. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  113. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  114. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  115. package/src/lib/types/components.ts +1 -0
  116. package/src/styles/01-settings/_index.scss +1 -0
  117. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  118. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  119. package/src/styles/02-tools/_tools.glass.scss +6 -0
  120. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  121. package/src/styles/06-components/_components.atomix-glass.scss +160 -99
  122. package/scripts/cli/__tests__/README.md +0 -81
  123. package/scripts/cli/__tests__/basic.test.js +0 -116
  124. package/scripts/cli/__tests__/clean.test.js +0 -278
  125. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  126. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  127. package/scripts/cli/__tests__/generator.test.js +0 -613
  128. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  129. package/scripts/cli/__tests__/integration.test.js +0 -938
  130. package/scripts/cli/__tests__/migrate.test.js +0 -74
  131. package/scripts/cli/__tests__/security.test.js +0 -206
  132. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  133. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  134. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  135. package/scripts/cli/__tests__/utils.test.js +0 -165
  136. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  137. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  138. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  139. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  140. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  141. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  142. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  143. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  144. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  145. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  146. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  147. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  148. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  149. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  150. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  151. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  152. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  153. package/src/components/TypedButton/TypedButton.tsx +0 -39
  154. package/src/components/TypedButton/index.ts +0 -2
  155. package/src/lib/composables/useBreadcrumb.ts +0 -81
  156. package/src/lib/composables/useChartInteractions.ts +0 -123
  157. package/src/lib/composables/useChartPerformance.ts +0 -347
  158. package/src/lib/composables/useDropdown.ts +0 -338
  159. package/src/lib/composables/useModal.ts +0 -110
  160. package/src/lib/composables/useTypedButton.ts +0 -66
  161. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  162. package/src/lib/utils/displacement-generator.ts +0 -92
  163. package/src/lib/utils/memoryMonitor.ts +0 -191
  164. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  165. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  166. package/src/styles/06-components/_components.testbutton.scss +0 -212
  167. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  168. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -1,938 +0,0 @@
1
- /**
2
- * CLI Integration Tests
3
- * Comprehensive test coverage for generate command with options, validation, and edge cases
4
- */
5
-
6
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
7
- import { mkdtemp, rm, writeFile, mkdir, readFile } from 'fs/promises';
8
- import { existsSync, readFileSync } from 'fs';
9
- import { join, resolve } from 'path';
10
- import { tmpdir } from 'os';
11
- import { execSync } from 'child_process';
12
-
13
- // Mock console to avoid noise in tests
14
- const originalConsole = global.console;
15
-
16
- describe('CLI Integration Tests', () => {
17
- let tempDir;
18
-
19
- beforeEach(async () => {
20
- tempDir = await mkdtemp(join(tmpdir(), 'atomix-cli-test-'));
21
-
22
- // Create project structure with React indicators for proper framework detection
23
- await mkdir(join(tempDir, 'src/styles/01-settings'), { recursive: true });
24
- await mkdir(join(tempDir, 'src/lib/composables'), { recursive: true });
25
- await mkdir(join(tempDir, 'src/lib/types'), { recursive: true });
26
- await writeFile(join(tempDir, 'package.json'), JSON.stringify({
27
- name: 'test-app',
28
- version: '1.0.0',
29
- dependencies: {
30
- react: '^18.0.0'
31
- }
32
- }));
33
-
34
- // Mock console methods
35
- global.console = {
36
- ...originalConsole,
37
- log: vi.fn(),
38
- error: vi.fn(),
39
- warn: vi.fn()
40
- };
41
- });
42
-
43
- afterEach(async () => {
44
- await rm(tempDir, { recursive: true, force: true });
45
- global.console = originalConsole;
46
- vi.clearAllMocks();
47
- });
48
-
49
- describe('atomix generate component', () => {
50
- it('should generate a basic component', () => {
51
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
52
-
53
- execSync(`node ${cliPath} generate component TestButton`, {
54
- cwd: tempDir,
55
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
56
- stdio: 'pipe'
57
- });
58
-
59
- const componentDir = join(tempDir, 'src', 'components', 'TestButton');
60
- expect(existsSync(componentDir)).toBe(true);
61
- const hasComponentFile = existsSync(join(componentDir, 'TestButton.tsx')) ||
62
- existsSync(join(componentDir, 'TestButton.jsx')) ||
63
- existsSync(join(componentDir, 'TestButton.html'));
64
- expect(hasComponentFile).toBe(true);
65
- });
66
-
67
- it('should reject invalid component names', () => {
68
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
69
-
70
- let threw = false;
71
- try {
72
- execSync(`node ${cliPath} generate component 123Invalid`, {
73
- cwd: tempDir,
74
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
75
- stdio: 'pipe'
76
- });
77
- } catch {
78
- threw = true;
79
- }
80
- expect(threw).toBe(true);
81
- });
82
-
83
- it('should create component directory when generating', () => {
84
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
85
- execSync(`node ${cliPath} generate component DuplicateButton`, {
86
- cwd: tempDir,
87
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
88
- stdio: 'pipe'
89
- });
90
- const componentDir = join(tempDir, 'src', 'components', 'DuplicateButton');
91
- expect(existsSync(componentDir)).toBe(true);
92
- });
93
- });
94
-
95
- describe('atomix generate component --complexity', () => {
96
- it('should generate simple component', () => {
97
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
98
-
99
- execSync(`node ${cliPath} generate component SimpleInput --complexity simple`, {
100
- cwd: tempDir,
101
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
102
- stdio: 'pipe'
103
- });
104
-
105
- const componentDir = join(tempDir, 'src', 'components', 'SimpleInput');
106
- expect(existsSync(componentDir)).toBe(true);
107
- });
108
-
109
- it('should generate complex component', () => {
110
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
111
-
112
- execSync(`node ${cliPath} generate component ComplexCard --complexity complex`, {
113
- cwd: tempDir,
114
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
115
- stdio: 'pipe'
116
- });
117
-
118
- const componentDir = join(tempDir, 'src', 'components', 'ComplexCard');
119
- expect(existsSync(componentDir)).toBe(true);
120
- });
121
-
122
- it('should handle invalid complexity level', () => {
123
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
124
-
125
- let threw = false;
126
- try {
127
- execSync(`node ${cliPath} generate component Test --complexity extreme`, {
128
- cwd: tempDir,
129
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
130
- stdio: 'pipe'
131
- });
132
- } catch (error) {
133
- threw = true;
134
- // Verify error message contains helpful suggestions
135
- if (error.stderr) {
136
- const errorMsg = error.stderr.toString();
137
- expect(errorMsg.toLowerCase()).toContain('complexity');
138
- }
139
- }
140
- expect(threw).toBe(true);
141
- });
142
- });
143
-
144
- describe('atomix generate component --features', () => {
145
- it('should generate with storybook by default', () => {
146
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
147
-
148
- execSync(`node ${cliPath} generate component StoryButton`, {
149
- cwd: tempDir,
150
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
151
- stdio: 'pipe'
152
- });
153
-
154
- const componentDir = join(tempDir, 'src', 'components', 'StoryButton');
155
- expect(existsSync(componentDir)).toBe(true);
156
- // Story file should be created by default
157
- expect(existsSync(join(componentDir, 'StoryButton.stories.tsx'))).toBe(true);
158
- });
159
-
160
- it('should generate with tests when using complexity complex', () => {
161
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
162
-
163
- execSync(`node ${cliPath} generate component ButtonWithTests --complexity complex`, {
164
- cwd: tempDir,
165
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
166
- stdio: 'pipe'
167
- });
168
-
169
- const componentDir = join(tempDir, 'src', 'components', 'ButtonWithTests');
170
- expect(existsSync(componentDir)).toBe(true);
171
- // Complex components may include tests
172
- expect(existsSync(join(componentDir, 'ButtonWithTests.test.tsx')) || existsSync(join(componentDir, 'ButtonWithTests.tsx'))).toBe(true);
173
- });
174
-
175
- it('should generate hook by default', () => {
176
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
177
-
178
- execSync(`node ${cliPath} generate component WithHook`, {
179
- cwd: tempDir,
180
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
181
- stdio: 'pipe'
182
- });
183
-
184
- // Hook is generated by default to composables directory
185
- const hookFile = join(tempDir, 'src', 'lib', 'composables', 'useWithHook.ts');
186
- expect(existsSync(hookFile)).toBe(true);
187
- });
188
-
189
- it('should generate styles by default', () => {
190
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
191
-
192
- execSync(`node ${cliPath} generate component WithStyles`, {
193
- cwd: tempDir,
194
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
195
- stdio: 'pipe'
196
- });
197
-
198
- // Styles are generated by default (check for any settings file)
199
- const settingsDir = join(tempDir, 'src', 'styles', '01-settings');
200
- const hasSettingsFile = existsSync(settingsDir) &&
201
- existsSync(join(settingsDir, '_settings.withstyles.scss')) ||
202
- existsSync(join(settingsDir, '_settings.with-styles.scss'));
203
- expect(hasSettingsFile).toBe(true);
204
- });
205
- });
206
-
207
- describe('Feature Flag Combinations', () => {
208
- it('should generate with default features (storybook, hook, styles)', () => {
209
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
210
-
211
- execSync(`node ${cliPath} generate component DefaultFeatures`, {
212
- cwd: tempDir,
213
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
214
- stdio: 'pipe'
215
- });
216
-
217
- const componentDir = join(tempDir, 'src', 'components', 'DefaultFeatures');
218
- expect(existsSync(componentDir)).toBe(true);
219
- expect(existsSync(join(componentDir, 'DefaultFeatures.tsx'))).toBe(true);
220
- expect(existsSync(join(componentDir, 'DefaultFeatures.stories.tsx'))).toBe(true);
221
- expect(existsSync(join(tempDir, 'src', 'lib', 'composables', 'useDefaultFeatures.ts'))).toBe(true);
222
- });
223
-
224
- it('should support --complexity flag', () => {
225
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
226
-
227
- execSync(`node ${cliPath} generate component SimpleComponent --complexity simple`, {
228
- cwd: tempDir,
229
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
230
- stdio: 'pipe'
231
- });
232
-
233
- const componentDir = join(tempDir, 'src', 'components', 'SimpleComponent');
234
- expect(existsSync(componentDir)).toBe(true);
235
- });
236
-
237
- it('should support --validate flag', () => {
238
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
239
-
240
- execSync(`node ${cliPath} generate component ValidatedComponent --validate`, {
241
- cwd: tempDir,
242
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
243
- stdio: 'pipe'
244
- });
245
-
246
- const componentDir = join(tempDir, 'src', 'components', 'ValidatedComponent');
247
- expect(existsSync(componentDir)).toBe(true);
248
- });
249
-
250
- it('should support --path flag for custom output location', () => {
251
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
252
- const customPath = join(tempDir, 'custom', 'components');
253
-
254
- execSync(`node ${cliPath} generate component CustomPath --path ${customPath}`, {
255
- cwd: tempDir,
256
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
257
- stdio: 'pipe'
258
- });
259
-
260
- const componentDir = join(customPath, 'CustomPath');
261
- expect(existsSync(componentDir)).toBe(true);
262
- expect(existsSync(join(componentDir, 'CustomPath.tsx'))).toBe(true);
263
- });
264
-
265
- it('should support --prompt flag for AI generation', () => {
266
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
267
-
268
- // This would test AI generation if AI is configured
269
- // For now, just verify the flag is accepted
270
- let errorOutput = '';
271
- try {
272
- execSync(`node ${cliPath} generate component AITest --prompt "A simple button"`, {
273
- cwd: tempDir,
274
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true', ATOMIX_AI_MOCK: 'true' },
275
- stdio: 'pipe'
276
- });
277
- } catch (error) {
278
- errorOutput = error.stderr ? error.stderr.toString() : '';
279
- }
280
-
281
- // May fail if AI not configured, but flag should be recognized
282
- expect(errorOutput).not.toMatch(/unknown option.*--prompt/i);
283
- });
284
- });
285
-
286
- describe('atomix generate component --interactive', () => {
287
- it('should generate component via interactive prompts', async () => {
288
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
289
-
290
- // Interactive mode with default selections (pressing Enter for defaults)
291
- const mockInput = '\n\n\n';
292
-
293
- execSync(`echo -e "InteractiveButton${mockInput}" | node ${cliPath} generate component --interactive`, {
294
- cwd: tempDir,
295
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
296
- stdio: 'pipe'
297
- });
298
-
299
- const componentDir = join(tempDir, 'src', 'components', 'InteractiveButton');
300
- expect(existsSync(componentDir)).toBe(true);
301
- expect(existsSync(join(componentDir, 'InteractiveButton.tsx'))).toBe(true);
302
- });
303
-
304
- it('should validate component name in interactive mode', () => {
305
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
306
-
307
- // Invalid name starting with number should be rejected
308
- let threw = false;
309
- try {
310
- execSync(`echo -e "123Invalid\n" | node ${cliPath} generate component --interactive`, {
311
- cwd: tempDir,
312
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
313
- stdio: 'pipe'
314
- });
315
- } catch (error) {
316
- threw = true;
317
- if (error.stderr) {
318
- const errorMsg = error.stderr.toString();
319
- expect(errorMsg.toLowerCase()).toMatch(/(pascalcase|invalid|name)/);
320
- }
321
- }
322
- // May timeout or fail, both are acceptable
323
- expect(threw || true).toBe(true);
324
- });
325
-
326
- it('should accept complexity selection in interactive mode', async () => {
327
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
328
-
329
- // Select simple complexity (first option)
330
- execSync(`echo -e "SimpleComponent\n0\n\n" | node ${cliPath} generate component --interactive`, {
331
- cwd: tempDir,
332
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
333
- stdio: 'pipe'
334
- });
335
-
336
- const componentDir = join(tempDir, 'src', 'components', 'SimpleComponent');
337
- expect(existsSync(componentDir)).toBe(true);
338
- });
339
-
340
- it('should allow feature toggling in interactive mode', async () => {
341
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
342
-
343
- // Enable tests feature
344
- execSync(`echo -e "ComponentWithTests\n\n \n" | node ${cliPath} generate component --interactive`, {
345
- cwd: tempDir,
346
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
347
- stdio: 'pipe'
348
- });
349
-
350
- const componentDir = join(tempDir, 'src', 'components', 'ComponentWithTests');
351
- expect(existsSync(componentDir)).toBe(true);
352
- expect(existsSync(join(componentDir, 'ComponentWithTests.test.tsx'))).toBe(true);
353
- });
354
- });
355
-
356
- describe('atomix generate token', () => {
357
- it('should run generate token (creates component by current implementation)', () => {
358
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
359
- execSync(`node ${cliPath} generate token colors`, {
360
- cwd: tempDir,
361
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
362
- stdio: 'pipe'
363
- });
364
- const componentDir = join(tempDir, 'src', 'components', 'Colors');
365
- expect(existsSync(componentDir)).toBe(true);
366
- });
367
-
368
- it('should reject invalid token category name for component validation', () => {
369
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
370
- expect(() => {
371
- execSync(`node ${cliPath} generate token 123invalid`, {
372
- cwd: tempDir,
373
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
374
- stdio: 'pipe'
375
- });
376
- }).toThrow();
377
- });
378
- });
379
-
380
- describe('atomix generate component - Output Quality Verification', () => {
381
-
382
- it('should generate component with TypeScript type definitions', () => {
383
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
384
-
385
- execSync(`node ${cliPath} generate component TypedButton`, {
386
- cwd: tempDir,
387
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
388
- stdio: 'pipe'
389
- });
390
-
391
- const componentFile = join(tempDir, 'src', 'components', 'TypedButton', 'TypedButton.tsx');
392
- const content = readFileSync(componentFile, 'utf8');
393
-
394
- // Verify TypeScript types are imported and used
395
- expect(content).toMatch(/import\s+type\s+.*TypedButtonProps/);
396
- expect(content).toContain('forwardRef<');
397
- // Verify type is used in forwardRef generic
398
- expect(content).toMatch(/forwardRef<\s*\w+\s*,\s*\w+Props\s*>/);
399
- });
400
-
401
- it('should generate component with forwardRef implementation', () => {
402
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
403
-
404
- execSync(`node ${cliPath} generate component RefForwardingButton`, {
405
- cwd: tempDir,
406
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
407
- stdio: 'pipe'
408
- });
409
-
410
- const componentFile = join(tempDir, 'src', 'components', 'RefForwardingButton', 'RefForwardingButton.tsx');
411
- const content = readFileSync(componentFile, 'utf8');
412
-
413
- expect(content).toContain('forwardRef');
414
- expect(content).toMatch(/forwardRef\s*<\s*\w+/);
415
- });
416
-
417
- it('should generate component with displayName property', () => {
418
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
419
-
420
- execSync(`node ${cliPath} generate component DisplayNamedComponent`, {
421
- cwd: tempDir,
422
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
423
- stdio: 'pipe'
424
- });
425
-
426
- const componentFile = join(tempDir, 'src', 'components', 'DisplayNamedComponent', 'DisplayNamedComponent.tsx');
427
- const content = readFileSync(componentFile, 'utf8');
428
-
429
- expect(content).toContain('displayName');
430
- expect(content).toMatch(/\.displayName\s*=\s*['"]DisplayNamedComponent['"]/);
431
- });
432
-
433
- it('should generate component without hardcoded hex colors', () => {
434
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
435
-
436
- execSync(`node ${cliPath} generate component ThemedComponent`, {
437
- cwd: tempDir,
438
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
439
- stdio: 'pipe'
440
- });
441
-
442
- const componentFile = join(tempDir, 'src', 'components', 'ThemedComponent', 'ThemedComponent.tsx');
443
- const content = readFileSync(componentFile, 'utf8');
444
-
445
- // Should not contain hex color codes (basic check)
446
- expect(content).not.toMatch(/#[0-9a-fA-F]{6}/);
447
- });
448
-
449
- it('should generate component with JSDoc documentation', () => {
450
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
451
-
452
- execSync(`node ${cliPath} generate component DocumentedComponent`, {
453
- cwd: tempDir,
454
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
455
- stdio: 'pipe'
456
- });
457
-
458
- const componentFile = join(tempDir, 'src', 'components', 'DocumentedComponent', 'DocumentedComponent.tsx');
459
- const content = readFileSync(componentFile, 'utf8');
460
-
461
- expect(content).toContain('/**');
462
- expect(content).toContain('*/');
463
- });
464
-
465
- it('should generate composable hook with proper typing', () => {
466
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
467
-
468
- execSync(`node ${cliPath} generate component HookComponent`, {
469
- cwd: tempDir,
470
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
471
- stdio: 'pipe'
472
- });
473
-
474
- const hookFile = join(tempDir, 'src', 'lib', 'composables', 'useHookComponent.ts');
475
- const content = readFileSync(hookFile, 'utf8');
476
-
477
- expect(content).toContain('export function useHookComponent');
478
- // Should import and use typed props
479
- expect(content).toMatch(/import\s+.*HookComponentProps/);
480
- // Should have parameter type annotation
481
- expect(content).toMatch(/initialProps\??:\s*Partial<HookComponentProps>/);
482
- // Should return typed object
483
- expect(content).toMatch(/return\s*\{/);
484
- });
485
-
486
- it('should generate Storybook story with args and argTypes', () => {
487
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
488
-
489
- execSync(`node ${cliPath} generate component StoryComponent`, {
490
- cwd: tempDir,
491
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
492
- stdio: 'pipe'
493
- });
494
-
495
- const storyFile = join(tempDir, 'src', 'components', 'StoryComponent', 'StoryComponent.stories.tsx');
496
- const content = readFileSync(storyFile, 'utf8');
497
-
498
- expect(content).toContain('args:');
499
- expect(content).toContain('argTypes:');
500
- });
501
-
502
- it('should generate test file with basic structure when complexity is complex', () => {
503
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
504
-
505
- execSync(`node ${cliPath} generate component TestableComponent --complexity complex`, {
506
- cwd: tempDir,
507
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
508
- stdio: 'pipe'
509
- });
510
-
511
- const componentDir = join(tempDir, 'src', 'components', 'TestableComponent');
512
- expect(existsSync(componentDir)).toBe(true);
513
-
514
- const testFile = join(componentDir, 'TestableComponent.test.tsx');
515
- if (existsSync(testFile)) {
516
- const content = readFileSync(testFile, 'utf8');
517
- expect(content).toMatch(/(describe|it|test)\s*\(/);
518
- expect(content).toContain('TestableComponent');
519
- }
520
- // Test file may or may not exist depending on complexity level
521
- expect(true).toBe(true);
522
- });
523
- });
524
-
525
- describe('Error Scenarios', () => {
526
- it('should handle existing component directory', () => {
527
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
528
-
529
- // Generate first component
530
- execSync(`node ${cliPath} generate component ExistingComponent`, {
531
- cwd: tempDir,
532
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
533
- stdio: 'pipe'
534
- });
535
-
536
- // Try to generate again - should either overwrite or fail gracefully
537
- // Current implementation may overwrite, this documents expected behavior
538
- expect(() => {
539
- execSync(`node ${cliPath} generate component ExistingComponent`, {
540
- cwd: tempDir,
541
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
542
- stdio: 'pipe'
543
- });
544
- }).not.toThrow();
545
- });
546
-
547
- it('should handle permission errors', () => {
548
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
549
-
550
- // Create read-only directory (Unix-like systems)
551
- const readOnlyDir = join(tempDir, 'readonly');
552
- mkdir(readOnlyDir, { recursive: true });
553
-
554
- // This test is platform-specific and may not work on all systems
555
- expect(true).toBe(true);
556
- });
557
-
558
- it('should handle invalid output path', () => {
559
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
560
-
561
- let threw = false;
562
- try {
563
- execSync(`node ${cliPath} generate component Test --path /nonexistent/path/that/does/not/exist`, {
564
- cwd: tempDir,
565
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
566
- stdio: 'pipe'
567
- });
568
- } catch (error) {
569
- threw = true;
570
- // Verify error message contains helpful suggestions
571
- if (error.stderr) {
572
- const errorMsg = error.stderr.toString();
573
- expect(errorMsg.toLowerCase()).toContain('path');
574
- }
575
- }
576
- expect(threw).toBe(true);
577
- });
578
-
579
- it('should display actionable error suggestions', () => {
580
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
581
-
582
- let errorOutput = '';
583
- try {
584
- execSync(`node ${cliPath} generate component 123InvalidName`, {
585
- cwd: tempDir,
586
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
587
- stdio: 'pipe'
588
- });
589
- } catch (error) {
590
- errorOutput = error.stderr ? error.stderr.toString() : '';
591
- }
592
-
593
- // Error output should contain suggestions
594
- expect(errorOutput).toBeTruthy();
595
- // Suggestions typically include "Use PascalCase" etc.
596
- expect(errorOutput.length).toBeGreaterThan(0);
597
- });
598
- });
599
-
600
- describe('atomix generate component - Design Token Integration', () => {
601
- beforeEach(async () => {
602
- // Create design tokens fixture
603
- const tokensDir = join(tempDir, 'design-tokens');
604
- await mkdir(tokensDir, { recursive: true });
605
- await writeFile(
606
- join(tokensDir, 'tokens.json'),
607
- JSON.stringify({
608
- color: {
609
- primary: { value: '#007bff', name: 'Primary Blue' },
610
- secondary: { value: '#6c757d', name: 'Secondary Gray' }
611
- },
612
- spacing: {
613
- sm: { value: '8px', name: 'Small Spacing' },
614
- md: { value: '16px', name: 'Medium Spacing' },
615
- lg: { value: '24px', name: 'Large Spacing' }
616
- }
617
- })
618
- );
619
- });
620
-
621
- it('should load and reference design tokens in generated component', () => {
622
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
623
-
624
- execSync(`node ${cliPath} generate component TokenAwareComponent`, {
625
- cwd: tempDir,
626
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
627
- stdio: 'pipe'
628
- });
629
-
630
- const componentFile = join(tempDir, 'src', 'components', 'TokenAwareComponent', 'TokenAwareComponent.tsx');
631
- const content = readFileSync(componentFile, 'utf8');
632
-
633
- // Should reference design tokens or CSS variables
634
- expect(content).toMatch(/(token\.|var\(--|theme\.|\$[a-zA-Z])/);
635
- });
636
-
637
- it('should generate ITCSS settings file with SCSS variables', () => {
638
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
639
-
640
- execSync(`node ${cliPath} generate component ScssComponent`, {
641
- cwd: tempDir,
642
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
643
- stdio: 'pipe'
644
- });
645
-
646
- const settingsFiles = [
647
- join(tempDir, 'src', 'styles', '01-settings', '_settings.scsscomponent.scss'),
648
- join(tempDir, 'src', 'styles', '01-settings', '_settings.scss-component.scss'),
649
- join(tempDir, 'src', 'styles', '01-settings', '_settings.scss_component.scss')
650
- ];
651
-
652
- const existingFile = settingsFiles.find(f => existsSync(f));
653
- expect(existingFile).toBeTruthy();
654
-
655
- if (existingFile) {
656
- const content = readFileSync(existingFile, 'utf8');
657
- expect(content).toMatch(/\$[a-zA-Z]/); // SCSS variables
658
- }
659
- });
660
-
661
- it('should generate ITCSS component styles layer', () => {
662
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
663
-
664
- execSync(`node ${cliPath} generate component StyledComponent`, {
665
- cwd: tempDir,
666
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
667
- stdio: 'pipe'
668
- });
669
-
670
- const componentsFiles = [
671
- join(tempDir, 'src', 'styles', '06-components', '_components.styledcomponent.scss'),
672
- join(tempDir, 'src', 'styles', '06-components', '_components.styled-component.scss'),
673
- join(tempDir, 'src', 'styles', '06-components', '_components.styled_component.scss')
674
- ];
675
-
676
- const existingFile = componentsFiles.find(f => existsSync(f));
677
- expect(existingFile).toBeTruthy();
678
-
679
- if (existingFile) {
680
- const content = readFileSync(existingFile, 'utf8');
681
- // Should contain CSS class definitions
682
- expect(content).toMatch(/\.[a-zA-Z]/);
683
- }
684
- });
685
- });
686
-
687
- describe('atomix validate', () => {
688
- beforeEach(async () => {
689
- const stylesDir = join(tempDir, 'src', 'styles', '01-settings');
690
- await mkdir(stylesDir, { recursive: true });
691
- await writeFile(join(stylesDir, '_settings.colors.scss'), '$primary: blue !default;\n$secondary: red !default;');
692
- await writeFile(join(stylesDir, '_settings.typography.scss'), '$font-base: sans-serif !default;');
693
- });
694
-
695
- it('should run full validation audit', () => {
696
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
697
- const result = execSync(`node ${cliPath} validate`, {
698
- cwd: tempDir,
699
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
700
- encoding: 'utf8',
701
- stdio: 'pipe'
702
- });
703
- expect(result).toMatch(/No issues found|Summary:|Quality Audit|issues?/);
704
- });
705
- });
706
-
707
- describe('atomix doctor', () => {
708
- it('should run system diagnostics', () => {
709
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
710
- const result = execSync(`node ${cliPath} doctor`, {
711
- cwd: tempDir,
712
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
713
- encoding: 'utf8',
714
- stdio: 'pipe'
715
- });
716
- expect(result).toContain('Atomix Diagnostic Report');
717
- });
718
- });
719
-
720
- describe('Error Handling', () => {
721
- it('should handle missing command gracefully', () => {
722
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
723
-
724
- expect(() => {
725
- execSync(`node ${cliPath} non-existent-command`, {
726
- cwd: tempDir,
727
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
728
- stdio: 'pipe'
729
- });
730
- }).toThrow();
731
- });
732
-
733
- it('should show help for --help flag', () => {
734
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
735
-
736
- const result = execSync(`node ${cliPath} --help`, {
737
- cwd: tempDir,
738
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
739
- encoding: 'utf8',
740
- stdio: 'pipe'
741
- });
742
-
743
- expect(result).toContain('Atomix Design System CLI');
744
- expect(result).toContain('Commands:');
745
- });
746
-
747
- it('should show version for --version flag', () => {
748
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
749
-
750
- const result = execSync(`node ${cliPath} --version`, {
751
- cwd: tempDir,
752
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
753
- encoding: 'utf8',
754
- stdio: 'pipe'
755
- });
756
-
757
- expect(result).toMatch(/\d+\.\d+\.\d+/);
758
- });
759
- });
760
-
761
- describe('Framework-Specific Generation', () => {
762
- it('should detect React project and generate .tsx files', async () => {
763
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
764
-
765
- // Create React project indicator
766
- await writeFile(join(tempDir, 'package.json'), JSON.stringify({
767
- name: 'react-app',
768
- dependencies: { react: '^18.0.0' }
769
- }));
770
-
771
- execSync(`node ${cliPath} generate component ReactComponent`, {
772
- cwd: tempDir,
773
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
774
- stdio: 'pipe'
775
- });
776
-
777
- const componentFile = join(tempDir, 'src', 'components', 'ReactComponent', 'ReactComponent.tsx');
778
- expect(existsSync(componentFile)).toBe(true);
779
-
780
- const content = readFileSync(componentFile, 'utf8');
781
- expect(content).toContain('import React');
782
- expect(content).toContain('forwardRef');
783
- });
784
-
785
- it('should detect Next.js project and generate compatible components', async () => {
786
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
787
-
788
- // Create Next.js project indicator
789
- await writeFile(join(tempDir, 'package.json'), JSON.stringify({
790
- name: 'next-app',
791
- dependencies: { next: '^14.0.0', react: '^18.0.0' }
792
- }));
793
-
794
- execSync(`node ${cliPath} generate component NextComponent`, {
795
- cwd: tempDir,
796
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
797
- stdio: 'pipe'
798
- });
799
-
800
- const componentFile = join(tempDir, 'src', 'components', 'NextComponent', 'NextComponent.tsx');
801
- expect(existsSync(componentFile)).toBe(true);
802
-
803
- const content = readFileSync(componentFile, 'utf8');
804
- // Next.js components may use 'use client' directive
805
- expect(content).toMatch(/('use client'|export.*)/);
806
- });
807
-
808
- it('should detect vanilla JS project and generate .html/.js files', async () => {
809
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
810
-
811
- // Create vanilla project indicator (no React/Next)
812
- await writeFile(join(tempDir, 'package.json'), JSON.stringify({
813
- name: 'vanilla-app'
814
- }));
815
-
816
- execSync(`node ${cliPath} generate component VanillaComponent`, {
817
- cwd: tempDir,
818
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
819
- stdio: 'pipe'
820
- });
821
-
822
- const componentDir = join(tempDir, 'src', 'components', 'VanillaComponent');
823
- expect(existsSync(componentDir)).toBe(true);
824
-
825
- // Should generate HTML or JS instead of TSX
826
- const hasHtml = existsSync(join(componentDir, 'VanillaComponent.html'));
827
- const hasJs = existsSync(join(componentDir, 'VanillaComponent.js'));
828
- expect(hasHtml || hasJs).toBe(true);
829
- });
830
- });
831
-
832
- describe('Edge Cases and Security', () => {
833
- it('should handle very long component names', () => {
834
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
835
- const longName = 'VeryLongComponentNameThatTestsFileNameLimitsAndPathHandlingCapabilities';
836
-
837
- execSync(`node ${cliPath} generate component ${longName}`, {
838
- cwd: tempDir,
839
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
840
- stdio: 'pipe'
841
- });
842
-
843
- const componentDir = join(tempDir, 'src', 'components', longName);
844
- expect(existsSync(componentDir)).toBe(true);
845
- });
846
-
847
- it('should reject component names with special characters', () => {
848
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
849
-
850
- let threw = false;
851
- try {
852
- execSync(`node ${cliPath} generate component "Special@Component"`, {
853
- cwd: tempDir,
854
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
855
- stdio: 'pipe'
856
- });
857
- } catch (error) {
858
- threw = true;
859
- if (error.stderr) {
860
- const errorMsg = error.stderr.toString();
861
- expect(errorMsg.toLowerCase()).toMatch(/(invalid|special|character)/);
862
- }
863
- }
864
- expect(threw).toBe(true);
865
- });
866
-
867
- it('should handle component names with spaces in quotes', () => {
868
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
869
-
870
- // Spaces should be rejected or sanitized
871
- let threw = false;
872
- try {
873
- execSync(`node ${cliPath} generate component "Invalid Name"`, {
874
- cwd: tempDir,
875
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
876
- stdio: 'pipe'
877
- });
878
- } catch (error) {
879
- threw = true;
880
- }
881
- expect(threw).toBe(true);
882
- });
883
-
884
- it('should prevent path traversal attacks', () => {
885
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
886
-
887
- let threw = false;
888
- try {
889
- execSync(`node ${cliPath} generate component Test --path ../../etc`, {
890
- cwd: tempDir,
891
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
892
- stdio: 'pipe'
893
- });
894
- } catch (error) {
895
- threw = true;
896
- if (error.stderr) {
897
- const errorMsg = error.stderr.toString();
898
- expect(errorMsg.toLowerCase()).toMatch(/(path|traversal|security|outside)/);
899
- }
900
- }
901
- expect(threw).toBe(true);
902
- });
903
-
904
- it('should handle reserved JavaScript words', () => {
905
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
906
-
907
- // Reserved words should be rejected or handled gracefully
908
- let threw = false;
909
- try {
910
- execSync(`node ${cliPath} generate component "div"`, {
911
- cwd: tempDir,
912
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
913
- stdio: 'pipe'
914
- });
915
- } catch (error) {
916
- threw = true;
917
- }
918
- // May or may not throw depending on implementation
919
- expect(threw || true).toBe(true);
920
- });
921
-
922
- it('should handle camelCase component names by converting to PascalCase', () => {
923
- const cliPath = resolve(__dirname, '../../atomix-cli.js');
924
-
925
- execSync(`node ${cliPath} generate component camelCaseComponent`, {
926
- cwd: tempDir,
927
- env: { ...process.env, ATOMIX_SKIP_DEP_CHECK: 'true' },
928
- stdio: 'pipe'
929
- });
930
-
931
- // Should create directory with PascalCase
932
- const pascalCaseDir = join(tempDir, 'src', 'components', 'CamelCaseComponent');
933
- const camelCaseDir = join(tempDir, 'src', 'components', 'camelCaseComponent');
934
-
935
- expect(existsSync(pascalCaseDir) || existsSync(camelCaseDir)).toBe(true);
936
- });
937
- });
938
- });