@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,251 +0,0 @@
1
- /**
2
- * Token Manager Tests
3
- */
4
-
5
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
6
- import {
7
- listTokens,
8
- validateTokens,
9
- exportTokens,
10
- importTokens,
11
- fixTokens,
12
- setProjectRoot
13
- } from '../token-manager.js';
14
- import { mkdtemp, rm, writeFile, readFile, mkdir } from 'fs/promises';
15
- import { join } from 'path';
16
- import { tmpdir } from 'os';
17
-
18
- // Mock dependencies
19
- vi.mock('ora', () => ({
20
- default: vi.fn(() => ({
21
- start: vi.fn(() => ({
22
- succeed: vi.fn(),
23
- fail: vi.fn(),
24
- text: ''
25
- }))
26
- }))
27
- }));
28
-
29
- vi.mock('chalk', () => ({
30
- default: {
31
- green: vi.fn((text) => text),
32
- red: vi.fn((text) => text),
33
- yellow: vi.fn((text) => text),
34
- cyan: vi.fn((text) => text),
35
- gray: vi.fn((text) => text),
36
- bold: vi.fn((obj) => obj)
37
- }
38
- }));
39
-
40
- describe('Token Manager', () => {
41
- let tempDir;
42
- let tokenDir;
43
-
44
- beforeEach(async () => {
45
- const tmp = await mkdtemp(join(tmpdir(), 'atomix-test-'));
46
- // Ensure absolute path
47
- // On some systems/configs tmpdir might return symlinked path or weirdness
48
- const { resolve } = await import('path');
49
- tempDir = resolve(tmp);
50
-
51
- tokenDir = join(tempDir, 'src', 'styles', '01-settings');
52
- await mkdir(tokenDir, { recursive: true });
53
- setProjectRoot(tempDir);
54
- });
55
-
56
- afterEach(async () => {
57
- setProjectRoot('');
58
- await rm(tempDir, { recursive: true, force: true });
59
- vi.clearAllMocks();
60
- });
61
-
62
- describe('listTokens', () => {
63
- it('should list tokens from valid files', async () => {
64
- // Create test token files
65
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
66
- $primary-500: #7AFFD7 !default;
67
- $secondary-500: #FF5733 !default;
68
- --atomix-color-primary: #7AFFD7;
69
- --atomix-color-secondary: #FF5733;
70
- `);
71
-
72
- const result = await listTokens();
73
-
74
- expect(result.tokens).toHaveProperty('colors');
75
- // Check keys inside the category object
76
- const colorTokens = result.tokens.colors.tokens;
77
- expect(colorTokens).toHaveProperty('$primary-500');
78
- expect(colorTokens).toHaveProperty('--atomix-color-primary');
79
- });
80
-
81
- it('should handle missing files gracefully', async () => {
82
- const result = await listTokens();
83
- expect(result.tokens).toEqual({});
84
- expect(result.categoryCount).toBe(0);
85
- });
86
- });
87
-
88
- describe('validateTokens', () => {
89
- it('should detect hardcoded colors', async () => {
90
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
91
- $primary: #ffffff !default;
92
- $secondary: #000000 !default;
93
- background: #ff0000; // Hardcoded color
94
- `);
95
-
96
- const result = await validateTokens();
97
-
98
- expect(result.warnings).toContainEqual(
99
- expect.objectContaining({
100
- category: 'hardcoded-value',
101
- file: expect.stringContaining('_settings.colors.scss')
102
- })
103
- );
104
- });
105
-
106
- it('should detect missing !default flags', async () => {
107
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
108
- $primary: #7AFFD7; // Missing !default
109
- $secondary: #FF5733 !default;
110
- `);
111
-
112
- const result = await validateTokens();
113
-
114
- expect(result.issues).toContainEqual(
115
- expect.objectContaining({
116
- category: 'missing-default',
117
- file: expect.stringContaining('_settings.colors.scss')
118
- })
119
- );
120
- });
121
-
122
- it('should validate naming conventions', async () => {
123
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
124
- $Invalid-Name: #7AFFD7 !default;
125
- $valid_name: #FF5733 !default;
126
- `);
127
-
128
- const result = await validateTokens();
129
-
130
- expect(result.issues).toContainEqual(
131
- expect.objectContaining({
132
- category: 'naming-convention',
133
- file: expect.stringContaining('_settings.colors.scss')
134
- })
135
- );
136
- });
137
- });
138
-
139
- describe('exportTokens', () => {
140
- it('should export tokens as JSON', async () => {
141
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
142
- $primary-500: #7AFFD7 !default;
143
- --atomix-color-primary: #7AFFD7;
144
- `);
145
-
146
- const outputPath = join(tempDir, 'tokens.json');
147
- await exportTokens('json', outputPath);
148
-
149
- const exported = JSON.parse(await readFile(outputPath, 'utf8'));
150
- expect(exported).toHaveProperty('colors');
151
- expect(exported.colors).toHaveProperty('$primary-500');
152
- });
153
-
154
- it('should export tokens as CSS custom properties', async () => {
155
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
156
- $primary-500: #7AFFD7 !default;
157
- --atomix-color-primary: #7AFFD7;
158
- `);
159
-
160
- const outputPath = join(tempDir, 'tokens.css');
161
- await exportTokens('css', outputPath);
162
-
163
- const css = await readFile(outputPath, 'utf8');
164
- expect(css).toContain('--atomix-color-primary: #7AFFD7');
165
- });
166
-
167
- it('should export tokens as TypeScript', async () => {
168
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
169
- $primary-500: #7AFFD7 !default;
170
- --atomix-color-primary: #7AFFD7;
171
- `);
172
-
173
- const outputPath = join(tempDir, 'tokens.ts');
174
- await exportTokens('ts', outputPath);
175
-
176
- const ts = await readFile(outputPath, 'utf8');
177
- expect(ts).toContain('export interface');
178
- expect(ts).toContain('AtomixTokens');
179
- });
180
- });
181
-
182
- describe('importTokens', () => {
183
- it('should import tokens from JSON', async () => {
184
- const jsonPath = join(tempDir, 'tokens.json');
185
- await writeFile(jsonPath, JSON.stringify({
186
- colors: {
187
- '$primary-500': '#7AFFD7',
188
- '--atomix-color-primary': '#7AFFD7'
189
- }
190
- }));
191
-
192
- await importTokens(jsonPath);
193
-
194
- // Verify the tokens were written to SCSS files
195
- const colorsFile = await readFile(join(tokenDir, '_settings.colors.scss'), 'utf8');
196
- expect(colorsFile).toContain('$primary-500: #7AFFD7 !default');
197
- expect(colorsFile).toContain('--atomix-color-primary: #7AFFD7');
198
- });
199
-
200
- it('should import tokens from JavaScript', async () => {
201
- const jsPath = join(tempDir, 'tokens.js');
202
- await writeFile(jsPath, `
203
- export const tokens = {
204
- colors: {
205
- '$primary-500': '#7AFFD7',
206
- '--atomix-color-primary': '#7AFFD7'
207
- }
208
- };
209
- `);
210
-
211
- await importTokens(jsPath);
212
-
213
- const colorsFile = await readFile(join(tokenDir, '_settings.colors.scss'), 'utf8');
214
- expect(colorsFile).toContain('$primary-500: #7AFFD7 !default');
215
- });
216
-
217
- it('should handle malformed JSON gracefully', async () => {
218
- const jsonPath = join(tempDir, 'invalid.json');
219
- await writeFile(jsonPath, '{ invalid json }');
220
-
221
- await expect(importTokens(jsonPath)).rejects.toThrow();
222
- });
223
- });
224
-
225
- describe('fixTokens', () => {
226
- it('should add missing !default flags', async () => {
227
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
228
- $primary: #7AFFD7;
229
- $secondary: #FF5733 !default;
230
- `);
231
-
232
- await fixTokens();
233
-
234
- const content = await readFile(join(tokenDir, '_settings.colors.scss'), 'utf8');
235
- expect(content).toContain('$primary: var(--atomix-color-primary) !default');
236
- });
237
-
238
- it('should convert hardcoded colors to variables', async () => {
239
- await writeFile(join(tokenDir, '_settings.colors.scss'), `
240
- $primary: #7AFFD7 !default;
241
- background: #ffffff;
242
- `);
243
-
244
- await fixTokens();
245
-
246
- const content = await readFile(join(tokenDir, '_settings.colors.scss'), 'utf8');
247
- // Should replace hardcoded colors with variables
248
- expect(content).toContain('var(--atomix-color-text)');
249
- });
250
- });
251
- });
@@ -1,361 +0,0 @@
1
- /**
2
- * Token Provider & Validator Tests
3
- * Tests for Phase 1: Enhanced Design Token Integration
4
- */
5
-
6
- import { describe, it, expect, beforeEach } from 'vitest';
7
- import {
8
- TokenProvider,
9
- TOKEN_FORMATS,
10
- TOKEN_CATEGORIES,
11
- tokenProvider
12
- } from '../internal/tokens/token-provider.js';
13
- import {
14
- TokenValidator,
15
- VALIDATION_RULES,
16
- SEVERITY,
17
- tokenValidator
18
- } from '../internal/tokens/token-validator.js';
19
-
20
- describe('TokenProvider', () => {
21
- let provider;
22
-
23
- beforeEach(() => {
24
- provider = new TokenProvider();
25
- });
26
-
27
- describe('Initialization', () => {
28
- it('should create a new TokenProvider instance', () => {
29
- expect(provider).toBeInstanceOf(TokenProvider);
30
- });
31
-
32
- it('should have default options', () => {
33
- expect(provider.tokenPath).toBe('./design-tokens');
34
- expect(provider.format).toBe(TOKEN_FORMATS.JSON);
35
- expect(provider.tokens).toEqual({});
36
- });
37
-
38
- it('should accept custom options', () => {
39
- const customProvider = new TokenProvider({
40
- tokenPath: './custom-tokens',
41
- format: TOKEN_FORMATS.CSS
42
- });
43
-
44
- expect(customProvider.tokenPath).toBe('./custom-tokens');
45
- expect(customProvider.format).toBe(TOKEN_FORMATS.CSS);
46
- });
47
- });
48
-
49
- describe('Token Loading', () => {
50
- it('should throw error when file not found', async () => {
51
- await expect(provider.loadTokens('./non-existent.json'))
52
- .rejects
53
- .toThrow('Token file not found');
54
- });
55
-
56
- it('should categorize tokens correctly', () => {
57
- const categorizeToken = provider.categorizeToken.bind(provider);
58
-
59
- expect(categorizeToken('color-primary')).toBe(TOKEN_CATEGORIES.COLOR);
60
- expect(categorizeToken('space-sm')).toBe(TOKEN_CATEGORIES.SPACING);
61
- expect(categorizeToken('font-size-lg')).toBe(TOKEN_CATEGORIES.TYPOGRAPHY);
62
- expect(categorizeToken('shadow-md')).toBe(TOKEN_CATEGORIES.SHADOW);
63
- expect(categorizeToken('radius-lg')).toBe(TOKEN_CATEGORIES.RADIUS);
64
- expect(categorizeToken('duration-fast')).toBe(TOKEN_CATEGORIES.ANIMATION);
65
- });
66
-
67
- it('should infer token types from values', () => {
68
- const inferTokenType = provider.inferTokenType.bind(provider);
69
-
70
- expect(inferTokenType('#ff0000')).toBe('color');
71
- expect(inferTokenType('rgb(255, 0, 0)')).toBe('color');
72
- expect(inferTokenType('hsl(0, 100%, 50%)')).toBe('color');
73
- expect(inferTokenType('16px')).toBe('dimension');
74
- expect(inferTokenType('1rem')).toBe('dimension');
75
- expect(inferTokenType('300ms')).toBe('duration');
76
- expect(inferTokenType('0.5s')).toBe('duration');
77
- expect(inferTokenType(42)).toBe('number');
78
- });
79
- });
80
-
81
- describe('Token Merging', () => {
82
- it('should merge multiple token sets', () => {
83
- const existing = {
84
- color: { primary: { value: '#000' } },
85
- spacing: { sm: { value: '8px' } }
86
- };
87
-
88
- const newTokens = {
89
- color: { secondary: { value: '#fff' } },
90
- typography: { base: { value: '16px' } }
91
- };
92
-
93
- const merged = provider.mergeTokens(existing, newTokens);
94
-
95
- expect(merged.color.primary).toBeDefined();
96
- expect(merged.color.secondary).toBeDefined();
97
- expect(merged.spacing.sm).toBeDefined();
98
- expect(merged.typography.base).toBeDefined();
99
- });
100
- });
101
-
102
- describe('Token Export', () => {
103
- beforeEach(() => {
104
- provider.tokens = {
105
- color: {
106
- primary: { value: '#007bff', type: 'color' },
107
- secondary: { value: '#6c757d', type: 'color' }
108
- },
109
- spacing: {
110
- sm: { value: '8px', type: 'dimension' },
111
- lg: { value: '16px', type: 'dimension' }
112
- }
113
- };
114
- });
115
-
116
- it('should export to JSON format', () => {
117
- const json = provider.exportTokens(TOKEN_FORMATS.JSON, { pretty: false });
118
- const parsed = JSON.parse(json);
119
-
120
- expect(parsed.color.primary.value).toBe('#007bff');
121
- expect(parsed.spacing.lg.value).toBe('16px');
122
- });
123
-
124
- it('should export to CSS custom properties', () => {
125
- const css = provider.exportTokens(TOKEN_FORMATS.CSS, {
126
- selector: ':root',
127
- prefix: 'atomix'
128
- });
129
-
130
- expect(css).toContain(':root {');
131
- expect(css).toContain('--atomix-color-primary: #007bff;');
132
- expect(css).toContain('--atomix-spacing-lg: 16px;');
133
- });
134
-
135
- it('should export to W3C DTCG format', () => {
136
- const dtcg = provider.exportTokens(TOKEN_FORMATS.W3C_DTCG);
137
-
138
- expect(dtcg.$schema).toBe('https://design-tokens.org/schema.json');
139
- expect(dtcg.tokens.color.primary.value).toBe('#007bff');
140
- });
141
- });
142
- });
143
-
144
- describe('TokenValidator', () => {
145
- let validator;
146
-
147
- beforeEach(() => {
148
- validator = new TokenValidator();
149
- });
150
-
151
- describe('Initialization', () => {
152
- it('should create a new TokenValidator instance', () => {
153
- expect(validator).toBeInstanceOf(TokenValidator);
154
- });
155
-
156
- it('should register all built-in rules', () => {
157
- expect(validator.rules.size).toBeGreaterThan(0);
158
- expect(validator.rules.has('COLOR_CONTRAST')).toBe(true);
159
- expect(validator.rules.has('SEMANTIC_NAMING')).toBe(true);
160
- expect(validator.rules.has('NO_HARDCODED_COLORS')).toBe(true);
161
- });
162
-
163
- it('should have all rules enabled by default', () => {
164
- expect(validator.enabledRules.length).toBeGreaterThan(0);
165
- });
166
- });
167
-
168
- describe('Validation Rules', () => {
169
- it('should validate color contrast', () => {
170
- const tokens = {
171
- color: {
172
- text: { value: '#999999' },
173
- background: { value: '#ffffff' }
174
- }
175
- };
176
-
177
- const result = validator.validate(tokens);
178
-
179
- // Low contrast should be flagged
180
- const contrastIssue = result.issues.find(i => i.rule === 'color-contrast');
181
- expect(contrastIssue).toBeDefined();
182
- expect(contrastIssue.severity).toBe(SEVERITY.ERROR);
183
- });
184
-
185
- it('should detect semantic naming issues', () => {
186
- const tokens = {
187
- color: {
188
- blue: { value: '#0000ff' },
189
- red: { value: '#ff0000' }
190
- }
191
- };
192
-
193
- const result = validator.validate(tokens);
194
-
195
- const namingIssue = result.issues.find(i => i.rule === 'semantic-naming');
196
- expect(namingIssue).toBeDefined();
197
- expect(namingIssue.severity).toBe(SEVERITY.WARNING);
198
- });
199
-
200
- it('should pass semantic naming for brand colors', () => {
201
- const tokens = {
202
- color: {
203
- brandBlue: { value: '#0000ff' },
204
- primaryRed: { value: '#ff0000' }
205
- }
206
- };
207
-
208
- const result = validator.validate(tokens);
209
-
210
- // Filter out token-completeness warnings which are expected
211
- const namingIssue = result.issues.find(i => i.rule === 'semantic-naming');
212
- // Should not flag brand colors or primary/secondary prefixed colors
213
- expect(namingIssue).toBeUndefined();
214
- });
215
-
216
- it('should detect hardcoded colors in code', () => {
217
- const codeContent = `
218
- const styles = {
219
- color: '#ff0000',
220
- backgroundColor: 'rgb(0, 123, 255)',
221
- borderColor: 'hsl(120, 100%, 50%)'
222
- };
223
- `;
224
-
225
- const result = validator.validate({}, { codeContent });
226
-
227
- const hardcodedIssue = result.issues.find(i => i.rule === 'no-hardcoded-colors');
228
- expect(hardcodedIssue).toBeDefined();
229
- expect(hardcodedIssue.matches).toHaveLength(3);
230
- });
231
-
232
- it('should validate token completeness', () => {
233
- const tokens = {
234
- color: { primary: { value: '#000' } }
235
- // Missing spacing and typography
236
- };
237
-
238
- const result = validator.validate(tokens);
239
-
240
- const completenessIssues = result.issues.filter(i => i.rule === 'token-completeness');
241
- expect(completenessIssues.length).toBeGreaterThan(0);
242
- });
243
-
244
- it('should detect duplicate tokens', () => {
245
- const tokens = {
246
- color: {
247
- primary: { value: '#007bff' },
248
- main: { value: '#007bff' }
249
- },
250
- spacing: {
251
- sm: { value: '8px' },
252
- small: { value: '8px' }
253
- }
254
- };
255
-
256
- const result = validator.validate(tokens);
257
-
258
- const duplicateIssues = result.issues.filter(i => i.rule === 'duplicate-detection');
259
- expect(duplicateIssues.length).toBeGreaterThan(0);
260
- });
261
- });
262
-
263
- describe('Rule Management', () => {
264
- it('should toggle rules on/off', () => {
265
- const initialCount = validator.enabledRules.length;
266
-
267
- validator.toggleRule('COLOR_CONTRAST', false);
268
- expect(validator.enabledRules).not.toContain('COLOR_CONTRAST');
269
-
270
- validator.toggleRule('COLOR_CONTRAST', true);
271
- expect(validator.enabledRules).toContain('COLOR_CONTRAST');
272
- });
273
-
274
- it('should allow registering custom rules', () => {
275
- const customRule = {
276
- name: 'custom-rule',
277
- description: 'Custom validation rule',
278
- severity: SEVERITY.INFO,
279
- validate: (tokens) => []
280
- };
281
-
282
- validator.registerRule('CUSTOM_RULE', customRule);
283
-
284
- expect(validator.rules.has('CUSTOM_RULE')).toBe(true);
285
- });
286
-
287
- it('should reject invalid rules', () => {
288
- expect(() => {
289
- validator.registerRule('INVALID', { name: 'test' });
290
- }).toThrow('must have name, validate, and severity');
291
- });
292
- });
293
-
294
- describe('Validation Report', () => {
295
- it('should generate formatted report', () => {
296
- const tokens = {
297
- color: {
298
- lowContrast: { value: '#ccc' },
299
- background: { value: '#fff' }
300
- }
301
- };
302
-
303
- const result = validator.validate(tokens);
304
- const report = validator.getReport(result);
305
-
306
- expect(report).toContain('Token Validation Report');
307
- expect(report).toContain('Status:');
308
- expect(report).toContain('Summary:');
309
- expect(report).toContain('Errors:');
310
- });
311
- });
312
- });
313
-
314
- describe('Integration Tests', () => {
315
- it('should load and validate tokens together', async () => {
316
- const provider = new TokenProvider();
317
- const validator = new TokenValidator();
318
-
319
- // Simulate loading tokens
320
- provider.tokens = {
321
- color: {
322
- primary: { value: '#007bff', type: 'color' },
323
- success: { value: '#22c55e', type: 'color' }
324
- },
325
- spacing: {
326
- 4: { value: '1rem', type: 'dimension' },
327
- 8: { value: '2rem', type: 'dimension' }
328
- }
329
- };
330
-
331
- const tokens = provider.getAllTokens();
332
- const result = validator.validate(tokens);
333
-
334
- expect(result).toBeDefined();
335
- expect(typeof result.valid).toBe('boolean');
336
- expect(result.summary).toBeDefined();
337
- });
338
-
339
- it('should validate component with loaded tokens', () => {
340
- const provider = new TokenProvider();
341
- const validator = new TokenValidator();
342
-
343
- provider.tokens = {
344
- color: {
345
- primary: { value: '#007bff' }
346
- }
347
- };
348
-
349
- const cleanCode = `
350
- const Component = () => {
351
- return <div style={{ color: 'var(--color-primary)' }} />;
352
- };
353
- `;
354
-
355
- const result = validator.validateComponent(cleanCode, provider.getAllTokens());
356
-
357
- // Should not have hardcoded color errors
358
- const hardcodedIssue = result.issues.find(i => i.rule === 'no-hardcoded-colors');
359
- expect(hardcodedIssue).toBeUndefined();
360
- });
361
- });