@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.
- package/atomix.config.ts +12 -0
- package/build-tools/webpack-loader.js +5 -4
- package/dist/atomix.css +230 -83
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +1 -1
- package/dist/atomix.min.css.map +1 -1
- package/dist/build-tools/webpack-loader.js +5 -4
- package/dist/charts.d.ts +24 -23
- package/dist/charts.js +271 -369
- package/dist/charts.js.map +1 -1
- package/dist/config.d.ts +624 -0
- package/dist/config.js +59 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +3 -2
- package/dist/core.js +342 -382
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +4 -6
- package/dist/forms.js +233 -334
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +11 -2
- package/dist/heavy.js +406 -445
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +109 -65
- package/dist/index.esm.js +654 -748
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +621 -717
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/layout.js +59 -60
- package/dist/layout.js.map +1 -1
- package/dist/theme.js +4 -4
- package/dist/theme.js.map +1 -1
- package/package.json +24 -9
- package/scripts/atomix-cli.js +15 -1
- package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
- package/scripts/cli/__tests__/detector.test.js +50 -0
- package/scripts/cli/__tests__/template-engine.test.js +23 -0
- package/scripts/cli/__tests__/test-setup.js +1 -133
- package/scripts/cli/commands/doctor.js +15 -3
- package/scripts/cli/commands/generate.js +113 -51
- package/scripts/cli/internal/ai-engine.js +30 -10
- package/scripts/cli/internal/complexity-utils.js +60 -0
- package/scripts/cli/internal/component-validator.js +49 -16
- package/scripts/cli/internal/generator.js +89 -36
- package/scripts/cli/internal/hook-generator.js +5 -2
- package/scripts/cli/internal/itcss-generator.js +16 -12
- package/scripts/cli/templates/next-templates.js +81 -30
- package/scripts/cli/templates/storybook-templates.js +12 -2
- package/scripts/cli/utils/detector.js +45 -7
- package/scripts/cli/utils/diagnostics.js +78 -0
- package/scripts/cli/utils/telemetry.js +13 -0
- package/src/components/Accordion/Accordion.stories.tsx +4 -0
- package/src/components/AtomixGlass/AtomixGlass.tsx +188 -128
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +63 -91
- package/src/components/AtomixGlass/PerformanceDashboard.tsx +153 -201
- package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +9 -6
- package/src/components/AtomixGlass/glass-utils.ts +51 -1
- package/src/components/AtomixGlass/stories/AnimationFeatures.stories.tsx +52 -46
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +573 -236
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +88 -41
- package/src/components/AtomixGlass/stories/argTypes.ts +19 -19
- package/src/components/AtomixGlass/stories/shared-components.tsx +7 -12
- package/src/components/AtomixGlass/stories/types.ts +3 -3
- package/src/components/Button/Button.tsx +114 -57
- package/src/components/Callout/Callout.tsx +4 -4
- package/src/components/Chart/ChartRenderer.tsx +1 -1
- package/src/components/Chart/DonutChart.tsx +11 -8
- package/src/components/EdgePanel/EdgePanel.tsx +119 -115
- package/src/components/Form/Select.tsx +4 -4
- package/src/components/List/List.tsx +4 -4
- package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
- package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
- package/src/components/ProductReview/ProductReview.tsx +4 -2
- package/src/components/Rating/Rating.tsx +4 -2
- package/src/components/SectionIntro/SectionIntro.tsx +4 -2
- package/src/components/Steps/Steps.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +5 -5
- package/src/components/Testimonial/Testimonial.tsx +4 -2
- package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
- package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
- package/src/layouts/CssGrid/CssGrid.tsx +215 -0
- package/src/layouts/CssGrid/index.ts +8 -0
- package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
- package/src/layouts/CssGrid/scripts/index.js +43 -0
- package/src/layouts/Grid/scripts/Container.js +139 -0
- package/src/layouts/Grid/scripts/Grid.js +184 -0
- package/src/layouts/Grid/scripts/GridCol.js +273 -0
- package/src/layouts/Grid/scripts/Row.js +154 -0
- package/src/layouts/Grid/scripts/index.js +48 -0
- package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
- package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
- package/src/lib/composables/useAccordion.ts +5 -5
- package/src/lib/composables/useAtomixGlass.ts +111 -74
- package/src/lib/composables/useAtomixGlassStyles.ts +0 -2
- package/src/lib/composables/useBarChart.ts +2 -2
- package/src/lib/composables/useChart.ts +3 -2
- package/src/lib/composables/useChartToolbar.ts +48 -66
- package/src/lib/composables/useDataTable.ts +1 -1
- package/src/lib/composables/useDatePicker.ts +2 -2
- package/src/lib/composables/useEdgePanel.ts +45 -54
- package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
- package/src/lib/composables/usePhotoViewer.ts +2 -3
- package/src/lib/composables/usePieChart.ts +1 -1
- package/src/lib/composables/usePopover.ts +151 -139
- package/src/lib/composables/useSideMenu.ts +28 -41
- package/src/lib/composables/useSlider.ts +2 -6
- package/src/lib/composables/useTooltip.ts +2 -2
- package/src/lib/config/index.ts +39 -0
- package/src/lib/constants/components.ts +1 -0
- package/src/lib/theme/devtools/Comparator.tsx +1 -1
- package/src/lib/theme/devtools/Inspector.tsx +1 -1
- package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
- package/src/lib/types/components.ts +1 -0
- package/src/styles/01-settings/_index.scss +1 -0
- package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
- package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
- package/src/styles/02-tools/_tools.glass.scss +6 -0
- package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
- package/src/styles/06-components/_components.atomix-glass.scss +160 -99
- package/scripts/cli/__tests__/README.md +0 -81
- package/scripts/cli/__tests__/basic.test.js +0 -116
- package/scripts/cli/__tests__/clean.test.js +0 -278
- package/scripts/cli/__tests__/component-generator.test.js +0 -332
- package/scripts/cli/__tests__/component-validator.test.js +0 -433
- package/scripts/cli/__tests__/generator.test.js +0 -613
- package/scripts/cli/__tests__/glass-motion.test.js +0 -256
- package/scripts/cli/__tests__/integration.test.js +0 -938
- package/scripts/cli/__tests__/migrate.test.js +0 -74
- package/scripts/cli/__tests__/security.test.js +0 -206
- package/scripts/cli/__tests__/theme-bridge.test.js +0 -507
- package/scripts/cli/__tests__/token-manager.test.js +0 -251
- package/scripts/cli/__tests__/token-provider.test.js +0 -361
- package/scripts/cli/__tests__/utils.test.js +0 -165
- package/src/components/AtomixGlass/stories/AnimationTests.stories.tsx +0 -95
- package/src/components/AtomixGlass/stories/CardExamples.stories.tsx +0 -212
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +0 -131
- package/src/components/AtomixGlass/stories/DashboardExamples.stories.tsx +0 -348
- package/src/components/AtomixGlass/stories/EcommerceExamples.stories.tsx +0 -410
- package/src/components/AtomixGlass/stories/FormExamples.stories.tsx +0 -436
- package/src/components/AtomixGlass/stories/HeroExamples.stories.tsx +0 -264
- package/src/components/AtomixGlass/stories/InteractivePlayground.stories.tsx +0 -247
- package/src/components/AtomixGlass/stories/MobileUIExamples.stories.tsx +0 -418
- package/src/components/AtomixGlass/stories/ModalExamples.stories.tsx +0 -402
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +0 -1082
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +0 -497
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +0 -103
- package/src/components/AtomixGlass/stories/PresetGallery.stories.tsx +0 -335
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +0 -395
- package/src/components/AtomixGlass/stories/WidgetExamples.stories.tsx +0 -441
- package/src/components/TypedButton/TypedButton.stories.tsx +0 -59
- package/src/components/TypedButton/TypedButton.tsx +0 -39
- package/src/components/TypedButton/index.ts +0 -2
- package/src/lib/composables/useBreadcrumb.ts +0 -81
- package/src/lib/composables/useChartInteractions.ts +0 -123
- package/src/lib/composables/useChartPerformance.ts +0 -347
- package/src/lib/composables/useDropdown.ts +0 -338
- package/src/lib/composables/useModal.ts +0 -110
- package/src/lib/composables/useTypedButton.ts +0 -66
- package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
- package/src/lib/utils/displacement-generator.ts +0 -92
- package/src/lib/utils/memoryMonitor.ts +0 -191
- package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
- package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
- package/src/styles/06-components/_components.testbutton.scss +0 -212
- package/src/styles/06-components/_components.testtypecheck.scss +0 -212
- 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
|
-
});
|