@shohojdhara/atomix 0.5.0 → 0.5.1

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 (76) hide show
  1. package/dist/atomix.css +95 -69
  2. package/dist/atomix.css.map +1 -1
  3. package/dist/atomix.min.css +1 -1
  4. package/dist/atomix.min.css.map +1 -1
  5. package/dist/charts.d.ts +1 -0
  6. package/dist/charts.js +231 -332
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +1 -0
  9. package/dist/core.js +232 -333
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.d.ts +1 -0
  12. package/dist/forms.js +231 -332
  13. package/dist/forms.js.map +1 -1
  14. package/dist/heavy.d.ts +11 -2
  15. package/dist/heavy.js +233 -334
  16. package/dist/heavy.js.map +1 -1
  17. package/dist/index.d.ts +13 -2
  18. package/dist/index.esm.js +228 -327
  19. package/dist/index.esm.js.map +1 -1
  20. package/dist/index.js +227 -326
  21. package/dist/index.js.map +1 -1
  22. package/dist/index.min.js +1 -1
  23. package/dist/index.min.js.map +1 -1
  24. package/package.json +11 -1
  25. package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
  26. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +62 -90
  27. package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
  28. package/src/components/AtomixGlass/glass-utils.ts +50 -0
  29. package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
  30. package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
  31. package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
  32. package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
  33. package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
  34. package/src/components/AtomixGlass/stories/types.ts +3 -3
  35. package/src/lib/composables/useAtomixGlass.ts +108 -71
  36. package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
  37. package/src/lib/constants/components.ts +1 -0
  38. package/src/lib/types/components.ts +1 -0
  39. package/src/lib/utils/displacement-generator.ts +1 -1
  40. package/src/styles/06-components/_components.atomix-glass.scss +158 -97
  41. package/scripts/cli/__tests__/README.md +0 -81
  42. package/scripts/cli/__tests__/basic.test.js +0 -116
  43. package/scripts/cli/__tests__/clean.test.js +0 -278
  44. package/scripts/cli/__tests__/component-generator.test.js +0 -332
  45. package/scripts/cli/__tests__/component-validator.test.js +0 -433
  46. package/scripts/cli/__tests__/generator.test.js +0 -613
  47. package/scripts/cli/__tests__/glass-motion.test.js +0 -256
  48. package/scripts/cli/__tests__/integration.test.js +0 -938
  49. package/scripts/cli/__tests__/migrate.test.js +0 -74
  50. package/scripts/cli/__tests__/security.test.js +0 -206
  51. package/scripts/cli/__tests__/test-setup.js +0 -135
  52. package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
  53. package/scripts/cli/__tests__/token-manager.test.js +0 -251
  54. package/scripts/cli/__tests__/token-provider.test.js +0 -361
  55. package/scripts/cli/__tests__/utils.test.js +0 -165
  56. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +0 -216
  57. package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
  58. package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
  59. package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
  60. package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
  61. package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
  62. package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
  63. package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
  64. package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
  65. package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
  66. package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
  67. package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
  68. package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
  69. package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
  70. package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
  71. package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
  72. package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
  73. package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
  74. package/src/components/TypedButton/TypedButton.tsx +0 -39
  75. package/src/components/TypedButton/index.ts +0 -2
  76. package/src/lib/composables/useTypedButton.ts +0 -66
@@ -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
- });