@shohojdhara/atomix 0.3.14 → 0.3.15

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 (173) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/build-tools/EXAMPLES.md +372 -0
  3. package/build-tools/README.md +242 -0
  4. package/build-tools/__tests__/error-handler.test.js +230 -0
  5. package/build-tools/__tests__/index.test.js +141 -0
  6. package/build-tools/__tests__/rollup-plugin.test.js +194 -0
  7. package/build-tools/__tests__/utils.test.js +161 -0
  8. package/build-tools/__tests__/vite-plugin.test.js +129 -0
  9. package/build-tools/__tests__/webpack-loader.test.js +190 -0
  10. package/build-tools/error-handler.js +308 -0
  11. package/build-tools/index.d.ts +43 -0
  12. package/build-tools/index.js +88 -0
  13. package/build-tools/package.json +67 -0
  14. package/build-tools/rollup-plugin.js +236 -0
  15. package/build-tools/types.d.ts +163 -0
  16. package/build-tools/utils.js +203 -0
  17. package/build-tools/vite-plugin.js +161 -0
  18. package/build-tools/webpack-loader.js +123 -0
  19. package/dist/atomix.css +203 -90
  20. package/dist/atomix.css.map +1 -1
  21. package/dist/atomix.min.css +3 -3
  22. package/dist/atomix.min.css.map +1 -1
  23. package/dist/build-tools/EXAMPLES.md +372 -0
  24. package/dist/build-tools/README.md +242 -0
  25. package/dist/build-tools/__tests__/error-handler.test.js +230 -0
  26. package/dist/build-tools/__tests__/index.test.js +141 -0
  27. package/dist/build-tools/__tests__/rollup-plugin.test.js +194 -0
  28. package/dist/build-tools/__tests__/utils.test.js +161 -0
  29. package/dist/build-tools/__tests__/vite-plugin.test.js +129 -0
  30. package/dist/build-tools/__tests__/webpack-loader.test.js +190 -0
  31. package/dist/build-tools/error-handler.js +308 -0
  32. package/dist/build-tools/index.d.ts +43 -0
  33. package/dist/build-tools/index.js +88 -0
  34. package/dist/build-tools/package.json +67 -0
  35. package/dist/build-tools/rollup-plugin.js +236 -0
  36. package/dist/build-tools/types.d.ts +163 -0
  37. package/dist/build-tools/utils.js +203 -0
  38. package/dist/build-tools/vite-plugin.js +161 -0
  39. package/dist/build-tools/webpack-loader.js +123 -0
  40. package/dist/charts.d.ts +1 -1
  41. package/dist/charts.js +86 -57
  42. package/dist/charts.js.map +1 -1
  43. package/dist/core.d.ts +1 -1
  44. package/dist/core.js +136 -112
  45. package/dist/core.js.map +1 -1
  46. package/dist/forms.d.ts +2 -5
  47. package/dist/forms.js +140 -128
  48. package/dist/forms.js.map +1 -1
  49. package/dist/heavy.d.ts +1 -1
  50. package/dist/heavy.js +136 -112
  51. package/dist/heavy.js.map +1 -1
  52. package/dist/index.d.ts +9 -61
  53. package/dist/index.esm.js +237 -286
  54. package/dist/index.esm.js.map +1 -1
  55. package/dist/index.js +250 -299
  56. package/dist/index.js.map +1 -1
  57. package/dist/index.min.js +1 -1
  58. package/dist/index.min.js.map +1 -1
  59. package/package.json +23 -8
  60. package/scripts/atomix-cli.js +170 -73
  61. package/scripts/cli/__tests__/README.md +81 -0
  62. package/scripts/cli/__tests__/basic.test.js +115 -0
  63. package/scripts/cli/__tests__/component-generator.test.js +332 -0
  64. package/scripts/cli/__tests__/integration.test.js +327 -0
  65. package/scripts/cli/__tests__/test-setup.js +133 -0
  66. package/scripts/cli/__tests__/token-manager.test.js +251 -0
  67. package/scripts/cli/__tests__/utils.test.js +161 -0
  68. package/scripts/cli/component-generator.js +253 -299
  69. package/scripts/cli/dependency-checker.js +355 -0
  70. package/scripts/cli/interactive-init.js +46 -5
  71. package/scripts/cli/template-manager.js +0 -2
  72. package/scripts/cli/templates/common-templates.js +636 -0
  73. package/scripts/cli/templates/composable-templates.js +148 -126
  74. package/scripts/cli/templates/index.js +23 -16
  75. package/scripts/cli/templates/project-templates.js +151 -23
  76. package/scripts/cli/templates/react-templates.js +280 -210
  77. package/scripts/cli/templates/scss-templates.js +90 -91
  78. package/scripts/cli/templates/testing-templates.js +206 -27
  79. package/scripts/cli/templates/testing-utils.js +278 -0
  80. package/scripts/cli/templates/types-templates.js +70 -56
  81. package/scripts/cli/theme-bridge.js +8 -2
  82. package/scripts/cli/token-manager.js +318 -206
  83. package/scripts/cli/utils.js +0 -1
  84. package/src/components/Accordion/Accordion.stories.tsx +369 -870
  85. package/src/components/AtomixGlass/AtomixGlass.tsx +80 -39
  86. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +103 -81
  87. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +8 -7
  88. package/src/components/AtomixGlass/glass-utils.ts +2 -2
  89. package/src/components/AtomixGlass/shader-utils.ts +5 -0
  90. package/src/components/AtomixGlass/stories/Customization.stories.tsx +131 -0
  91. package/src/components/AtomixGlass/stories/Examples.stories.tsx +2957 -2853
  92. package/src/components/AtomixGlass/stories/Modes.stories.tsx +1 -1
  93. package/src/components/AtomixGlass/stories/Overview.stories.tsx +348 -0
  94. package/src/components/AtomixGlass/stories/Performance.stories.tsx +103 -0
  95. package/src/components/AtomixGlass/stories/Playground.stories.tsx +50 -35
  96. package/src/components/AtomixGlass/stories/{ShaderVariants.stories.tsx → Shaders.stories.tsx} +1 -1
  97. package/src/components/AtomixGlass/stories/shared-components.tsx +90 -190
  98. package/src/components/Avatar/Avatar.stories.tsx +213 -1
  99. package/src/components/Badge/Badge.stories.tsx +121 -362
  100. package/src/components/Block/Block.stories.tsx +21 -12
  101. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +141 -23
  102. package/src/components/Button/Button.stories.tsx +463 -1126
  103. package/src/components/Button/Button.test.tsx +107 -0
  104. package/src/components/Button/Button.tsx +46 -50
  105. package/src/components/Button/ButtonGroup.stories.tsx +373 -217
  106. package/src/components/Callout/Callout.stories.tsx +289 -634
  107. package/src/components/Card/Card.stories.tsx +248 -68
  108. package/src/components/Chart/Chart.stories.tsx +150 -8
  109. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +151 -69
  110. package/src/components/Countdown/Countdown.stories.tsx +115 -8
  111. package/src/components/DataTable/DataTable.stories.tsx +346 -146
  112. package/src/components/DatePicker/DatePicker.stories.tsx +325 -1066
  113. package/src/components/Dropdown/Dropdown.stories.tsx +153 -33
  114. package/src/components/EdgePanel/EdgePanel.stories.tsx +230 -21
  115. package/src/components/Footer/Footer.stories.tsx +392 -328
  116. package/src/components/Form/Checkbox.stories.tsx +140 -6
  117. package/src/components/Form/Checkbox.test.tsx +63 -0
  118. package/src/components/Form/Checkbox.tsx +87 -51
  119. package/src/components/Form/Form.stories.tsx +119 -20
  120. package/src/components/Form/FormGroup.stories.tsx +127 -4
  121. package/src/components/Form/Radio.stories.tsx +140 -5
  122. package/src/components/Form/Select.stories.tsx +140 -8
  123. package/src/components/Form/Textarea.stories.tsx +149 -6
  124. package/src/components/Hero/Hero.stories.tsx +333 -32
  125. package/src/components/List/List.stories.tsx +141 -3
  126. package/src/components/Modal/Modal.stories.tsx +181 -42
  127. package/src/components/Popover/Popover.stories.tsx +448 -98
  128. package/src/components/Progress/Progress.stories.tsx +167 -5
  129. package/src/components/River/River.stories.tsx +1 -1
  130. package/src/components/SectionIntro/SectionIntro.stories.tsx +240 -48
  131. package/src/components/Spinner/Spinner.stories.tsx +102 -8
  132. package/src/components/Steps/Steps.stories.tsx +172 -43
  133. package/src/components/Tabs/Tabs.stories.tsx +136 -10
  134. package/src/components/Testimonial/Testimonial.stories.tsx +120 -3
  135. package/src/components/Todo/Todo.stories.tsx +198 -9
  136. package/src/components/Toggle/Toggle.stories.tsx +126 -39
  137. package/src/components/Tooltip/Tooltip.stories.tsx +194 -104
  138. package/src/components/Upload/Upload.stories.tsx +113 -24
  139. package/src/lib/README.md +2 -2
  140. package/src/lib/__tests__/theme-tools.test.ts +193 -0
  141. package/src/lib/composables/index.ts +2 -2
  142. package/src/lib/composables/useAtomixGlass.ts +28 -56
  143. package/src/lib/composables/useChartExport.ts +2 -7
  144. package/src/lib/composables/useDataTable.ts +46 -29
  145. package/src/lib/constants/components.ts +9 -32
  146. package/src/lib/theme/devtools/CLI.ts +1 -1
  147. package/src/lib/types/components.ts +1 -1
  148. package/src/lib/utils/__tests__/csv.test.ts +45 -0
  149. package/src/lib/utils/csv.ts +17 -0
  150. package/src/lib/utils/dataTableExport.ts +1 -10
  151. package/src/styles/01-settings/_index.scss +2 -1
  152. package/src/styles/01-settings/_settings.accordion.scss +28 -7
  153. package/src/styles/01-settings/_settings.colors.scss +11 -11
  154. package/src/styles/01-settings/_settings.typography.scss +5 -5
  155. package/src/styles/02-tools/_tools.utility-api.scss +14 -0
  156. package/src/styles/06-components/_components.accordion.scss +56 -14
  157. package/src/styles/06-components/_components.checkbox.scss +23 -17
  158. package/src/styles/99-utilities/_index.scss +2 -0
  159. package/src/styles/99-utilities/_utilities.scss +3 -1
  160. package/src/styles/99-utilities/_utilities.text-gradient.scss +45 -0
  161. package/themes/dark-complementary/README.md +98 -0
  162. package/themes/dark-complementary/index.scss +158 -0
  163. package/themes/default-light/README.md +81 -0
  164. package/themes/default-light/index.scss +154 -0
  165. package/themes/high-contrast/README.md +105 -0
  166. package/themes/high-contrast/index.scss +172 -0
  167. package/themes/test-theme/README.md +38 -0
  168. package/themes/test-theme/index.scss +47 -0
  169. package/scripts/cli/templates-original-backup.js +0 -1655
  170. package/scripts/cli/templates_backup.js +0 -684
  171. package/src/components/AtomixGlass/stories/AtomixGlass.stories.tsx +0 -1438
  172. package/src/lib/composables/useButton.ts +0 -93
  173. package/src/lib/composables/useCheckbox.ts +0 -70
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Basic CLI Tests - Core Functionality
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ validatePath,
8
+ validateComponentName,
9
+ validateThemeName,
10
+ sanitizeInput,
11
+ AtomixCLIError
12
+ } from '../utils.js';
13
+
14
+ describe('CLI Core Utils', () => {
15
+ describe('validatePath', () => {
16
+ it('should accept valid relative paths', () => {
17
+ const result = validatePath('./src/components', '/project');
18
+ expect(result.isValid).toBe(true);
19
+ });
20
+
21
+ it('should reject paths outside project directory', () => {
22
+ const result = validatePath('../../etc/passwd', '/project');
23
+ expect(result.isValid).toBe(false);
24
+ expect(result.error).toContain('outside the project directory');
25
+ });
26
+
27
+ it('should reject sensitive files', () => {
28
+ const result = validatePath('.env', '/project');
29
+ expect(result.isValid).toBe(false);
30
+ expect(result.error).toContain('sensitive path');
31
+ });
32
+ });
33
+
34
+ describe('validateComponentName', () => {
35
+ it('should accept valid PascalCase names', () => {
36
+ const validNames = ['Button', 'CardHeader', 'ModalDialog'];
37
+
38
+ validNames.forEach(name => {
39
+ const result = validateComponentName(name);
40
+ expect(result.isValid).toBe(true);
41
+ });
42
+ });
43
+
44
+ it('should reject invalid names', () => {
45
+ const invalidNames = ['button', 'button-primary', 'Button-Primary', '123Button', ''];
46
+
47
+ invalidNames.forEach(name => {
48
+ const result = validateComponentName(name);
49
+ expect(result.isValid).toBe(false);
50
+ });
51
+ });
52
+
53
+ it('should reject reserved words', () => {
54
+ const reservedWords = ['Component', 'React', 'Fragment'];
55
+
56
+ reservedWords.forEach(name => {
57
+ const result = validateComponentName(name);
58
+ expect(result.isValid).toBe(false);
59
+ });
60
+ });
61
+ });
62
+
63
+ describe('validateThemeName', () => {
64
+ it('should accept valid kebab-case names', () => {
65
+ const validNames = ['dark-theme', 'light', 'high-contrast'];
66
+
67
+ validNames.forEach(name => {
68
+ const result = validateThemeName(name);
69
+ expect(result.isValid).toBe(true);
70
+ });
71
+ });
72
+
73
+ it('should reject invalid theme names', () => {
74
+ const invalidNames = ['DarkTheme', 'theme_dark', 'Theme-Dark', '123theme', ''];
75
+
76
+ invalidNames.forEach(name => {
77
+ const result = validateThemeName(name);
78
+ expect(result.isValid).toBe(false);
79
+ });
80
+ });
81
+ });
82
+
83
+ describe('sanitizeInput', () => {
84
+ it('should remove dangerous shell characters', () => {
85
+ const dangerousInputs = [
86
+ 'test; rm -rf /',
87
+ 'input && malicious-command',
88
+ 'command | pipe'
89
+ ];
90
+
91
+ dangerousInputs.forEach(input => {
92
+ const sanitized = sanitizeInput(input);
93
+ expect(sanitized).not.toMatch(/[;&|`$<>]/);
94
+ });
95
+ });
96
+
97
+ it('should preserve safe characters', () => {
98
+ const safeInput = 'Button-Component_123';
99
+ const sanitized = sanitizeInput(safeInput);
100
+ expect(sanitized).toBe(safeInput);
101
+ });
102
+ });
103
+
104
+ describe('AtomixCLIError', () => {
105
+ it('should create error with code and suggestions', () => {
106
+ const suggestions = ['Use PascalCase', 'Start with letter'];
107
+ const error = new AtomixCLIError('Invalid name', 'INVALID_NAME', suggestions);
108
+
109
+ expect(error.message).toBe('Invalid name');
110
+ expect(error.code).toBe('INVALID_NAME');
111
+ expect(error.suggestions).toEqual(suggestions);
112
+ expect(error.name).toBe('AtomixCLIError');
113
+ });
114
+ });
115
+ });
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Component Generator Tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
+ import {
7
+ generateComponentByComplexity,
8
+ interactiveComponentGeneration,
9
+ validateGeneratedComponent,
10
+ displayValidationReport
11
+ } from '../component-generator.js';
12
+ import { mkdtemp, rm, writeFile, mkdir } from 'fs/promises';
13
+ import { join } from 'path';
14
+ import { tmpdir } from 'os';
15
+
16
+ // Mock dependencies
17
+ vi.mock('ora', () => ({
18
+ default: vi.fn(() => ({
19
+ start: vi.fn(() => ({
20
+ succeed: vi.fn(),
21
+ fail: vi.fn(),
22
+ text: ''
23
+ }))
24
+ }))
25
+ }));
26
+
27
+ vi.mock('inquirer', async () => {
28
+ const actual = await vi.importActual('inquirer');
29
+ return {
30
+ ...actual,
31
+ default: {
32
+ prompt: vi.fn()
33
+ }
34
+ };
35
+ });
36
+
37
+ vi.mock('chalk', () => {
38
+ const bold = vi.fn((text) => text);
39
+ bold.cyan = vi.fn((text) => text);
40
+ bold.green = vi.fn((text) => text);
41
+ bold.yellow = vi.fn((text) => text);
42
+ bold.red = vi.fn((text) => text);
43
+
44
+ return {
45
+ default: {
46
+ green: vi.fn((text) => text),
47
+ red: vi.fn((text) => text),
48
+ yellow: vi.fn((text) => text),
49
+ cyan: vi.fn((text) => text),
50
+ gray: vi.fn((text) => text),
51
+ bold: bold
52
+ }
53
+ };
54
+ });
55
+
56
+ describe('Component Generator', () => {
57
+ let tempDir;
58
+ let componentsDir;
59
+
60
+ beforeEach(async () => {
61
+ tempDir = await mkdtemp(join(tmpdir(), 'atomix-test-'));
62
+ componentsDir = join(tempDir, 'src', 'components');
63
+ await mkdir(componentsDir, { recursive: true });
64
+ });
65
+
66
+ afterEach(async () => {
67
+ await rm(tempDir, { recursive: true, force: true });
68
+ vi.clearAllMocks();
69
+ });
70
+
71
+ describe('generateComponentByComplexity', () => {
72
+ it('should generate simple component', async () => {
73
+ const componentName = 'SimpleButton';
74
+ const options = {
75
+ typescript: true,
76
+ story: false,
77
+ test: false,
78
+ path: componentsDir
79
+ };
80
+
81
+ const result = generateComponentByComplexity(componentName, 'simple', options);
82
+
83
+ // Check returned template content
84
+ expect(result).toContain('export const SimpleButton');
85
+ expect(result).toContain('forwardRef');
86
+ });
87
+
88
+ it('should generate medium complexity component', async () => {
89
+ const componentName = 'MediumCard';
90
+ const options = {
91
+ typescript: true,
92
+ story: true,
93
+ test: false,
94
+ path: componentsDir
95
+ };
96
+
97
+ const result = generateComponentByComplexity(componentName, 'medium', options);
98
+
99
+ // Check returned template content
100
+ expect(result).toContain('useId');
101
+ expect(result).toContain('MediumCardProps');
102
+ });
103
+
104
+ it('should generate complex component', async () => {
105
+ const componentName = 'ComplexModal';
106
+ const options = {
107
+ typescript: true,
108
+ story: true,
109
+ test: true,
110
+ path: componentsDir
111
+ };
112
+
113
+ const result = generateComponentByComplexity(componentName, 'complex', options);
114
+
115
+ // Check returned template content
116
+ expect(result).toContain('useId');
117
+ expect(result).toContain('AtomixGlass');
118
+ expect(result).toContain('ComplexModalProps');
119
+ });
120
+
121
+ it('should generate SCSS files', async () => {
122
+ // Note: generateComponentByComplexity returns component template only
123
+ // SCSS generation is handled separately by the CLI
124
+ const componentName = 'StyledButton';
125
+ const options = {
126
+ typescript: true,
127
+ scssModule: false,
128
+ path: componentsDir
129
+ };
130
+
131
+ const result = generateComponentByComplexity(componentName, 'simple', options);
132
+
133
+ // Should return valid component code
134
+ expect(result).toContain('export const StyledButton');
135
+ expect(result).toContain('forwardRef');
136
+ });
137
+
138
+ it('should generate Storybook stories', async () => {
139
+ // Note: Story files are generated separately by the CLI
140
+ const componentName = 'StoryComponent';
141
+ const options = {
142
+ typescript: true,
143
+ story: true,
144
+ path: componentsDir
145
+ };
146
+
147
+ const result = generateComponentByComplexity(componentName, 'simple', options);
148
+
149
+ // Should return valid component code that can be used in stories
150
+ expect(result).toContain('export const StoryComponent');
151
+ });
152
+
153
+ it('should generate test files', async () => {
154
+ // Note: Test files are generated separately by the CLI
155
+ const componentName = 'TestComponent';
156
+ const options = {
157
+ typescript: true,
158
+ test: true,
159
+ path: componentsDir
160
+ };
161
+
162
+ const result = generateComponentByComplexity(componentName, 'simple', options);
163
+
164
+ // Should return valid component code that can be tested
165
+ expect(result).toContain('export const TestComponent');
166
+ });
167
+ });
168
+
169
+ describe('interactiveComponentGeneration', () => {
170
+ it('should prompt for component details', async () => {
171
+ const inquirer = (await import('inquirer')).default;
172
+ inquirer.prompt.mockResolvedValue({
173
+ componentName: 'InteractiveButton',
174
+ complexity: 'medium',
175
+ features: ['typescript', 'story', 'test'],
176
+ outputPath: './src/components'
177
+ });
178
+
179
+ const result = await interactiveComponentGeneration();
180
+
181
+ expect(result).toEqual({
182
+ name: 'InteractiveButton',
183
+ complexity: 'medium',
184
+ features: ['typescript', 'story', 'test'],
185
+ outputPath: './src/components'
186
+ });
187
+
188
+ expect(inquirer.prompt).toHaveBeenCalledWith(
189
+ expect.arrayContaining([
190
+ expect.objectContaining({
191
+ name: 'componentName',
192
+ type: 'input'
193
+ })
194
+ ])
195
+ );
196
+ });
197
+
198
+ it('should handle cancellation', async () => {
199
+ const inquirer = (await import('inquirer')).default;
200
+ inquirer.prompt.mockRejectedValue(new Error('User cancelled'));
201
+
202
+ const result = await interactiveComponentGeneration();
203
+ expect(result).toBeNull();
204
+ });
205
+ });
206
+
207
+ describe('validateGeneratedComponent', () => {
208
+ it('should validate component structure', async () => {
209
+ const componentName = 'ValidComponent';
210
+ const componentPath = join(componentsDir, componentName);
211
+
212
+ // Create valid component structure
213
+ await mkdir(componentPath, { recursive: true });
214
+ await writeFile(join(componentPath, `${componentName}.tsx`), `
215
+ import React, { forwardRef } from 'react';
216
+
217
+ export const ValidComponent = forwardRef<HTMLDivElement, ValidComponentProps>(
218
+ ({ children }, ref) => {
219
+ return <div ref={ref}>{children}</div>;
220
+ }
221
+ );
222
+
223
+ export interface ValidComponentProps {
224
+ children?: React.ReactNode;
225
+ }
226
+ `);
227
+
228
+ const validation = await validateGeneratedComponent(componentName, componentPath);
229
+
230
+ expect(validation.issues).toHaveLength(0);
231
+ expect(validation.valid).toBe(true);
232
+ });
233
+
234
+ it('should detect missing TypeScript interface', async () => {
235
+ const componentName = 'InvalidComponent';
236
+ const componentPath = join(componentsDir, componentName);
237
+
238
+ await mkdir(componentPath, { recursive: true });
239
+ await writeFile(join(componentPath, `${componentName}.tsx`), `
240
+ export const InvalidComponent = ({ children }) => {
241
+ return <div>{children}</div>;
242
+ };
243
+ `);
244
+
245
+ const validation = await validateGeneratedComponent(componentName, componentPath);
246
+
247
+ expect(validation.issues).toEqual(
248
+ expect.arrayContaining([
249
+ expect.objectContaining({ issue: 'Missing TypeScript type definitions' })
250
+ ])
251
+ );
252
+ expect(validation.valid).toBe(false);
253
+ });
254
+
255
+ it('should detect missing accessibility attributes', async () => {
256
+ const componentName = 'InaccessibleComponent';
257
+ const componentPath = join(componentsDir, componentName);
258
+
259
+ await mkdir(componentPath, { recursive: true });
260
+ await writeFile(join(componentPath, `${componentName}.tsx`), `
261
+ export const InaccessibleComponent = () => {
262
+ return <button>Click me</button>;
263
+ };
264
+ `);
265
+
266
+ const validation = await validateGeneratedComponent(componentName, componentPath);
267
+
268
+ expect(validation.warnings).toEqual(
269
+ expect.arrayContaining([
270
+ expect.objectContaining({ issue: 'Missing accessibility attributes' })
271
+ ])
272
+ );
273
+ });
274
+
275
+ it('should detect missing design tokens', async () => {
276
+ const componentName = 'HardcodedComponent';
277
+ const componentPath = join(componentsDir, componentName);
278
+
279
+ await mkdir(componentPath, { recursive: true });
280
+ await writeFile(join(componentPath, `${componentName}.tsx`), `
281
+ export const HardcodedComponent = () => {
282
+ return <div style={{ color: '#ff0000', padding: '16px' }}>Content</div>;
283
+ };
284
+ `);
285
+
286
+ const validation = await validateGeneratedComponent(componentName, componentPath);
287
+
288
+ expect(validation.warnings).toEqual(
289
+ expect.arrayContaining([
290
+ expect.objectContaining({ issue: 'Using hardcoded values instead of design tokens' })
291
+ ])
292
+ );
293
+ });
294
+ });
295
+
296
+ describe('displayValidationReport', () => {
297
+ it('should display validation report with issues', () => {
298
+ const issues = ['Missing interface', 'No accessibility'];
299
+ const warnings = ['Hardcoded styles'];
300
+
301
+ const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
302
+
303
+ displayValidationReport({
304
+ valid: false,
305
+ issues: issues.map(i => ({ file: 'test', issue: i, suggestion: 'fix' })),
306
+ warnings: warnings.map(w => ({ file: 'test', issue: w, suggestion: 'fix' }))
307
+ });
308
+
309
+ expect(consoleSpy).toHaveBeenCalledWith(
310
+ expect.stringContaining('❌ Found 2 issue(s):')
311
+ );
312
+ expect(consoleSpy).toHaveBeenCalledWith(
313
+ expect.stringContaining('⚠️ Found 1 warning(s):')
314
+ );
315
+
316
+ consoleSpy.mockRestore();
317
+ });
318
+
319
+ it('should display success message for valid components', () => {
320
+ const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
321
+
322
+ const isValid = displayValidationReport({ valid: true, issues: [], warnings: [] });
323
+
324
+ expect(isValid).toBe(true);
325
+ expect(consoleSpy).toHaveBeenCalledWith(
326
+ expect.stringContaining('✅ Component validation passed!')
327
+ );
328
+
329
+ consoleSpy.mockRestore();
330
+ });
331
+ });
332
+ });