@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,278 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Atomix CLI Clean Command
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
-
import { join } from 'path';
|
|
7
|
-
import { mkdtemp, writeFile, mkdir, rm } from 'fs/promises';
|
|
8
|
-
import { tmpdir } from 'os';
|
|
9
|
-
import { cacheManager } from '../utils/cache-manager.js';
|
|
10
|
-
|
|
11
|
-
describe('cacheManager', () => {
|
|
12
|
-
let testDir;
|
|
13
|
-
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
// Create isolated temp directory for each test
|
|
16
|
-
testDir = await mkdtemp(join(tmpdir(), 'atomix-clean-test-'));
|
|
17
|
-
process.chdir(testDir);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(async () => {
|
|
21
|
-
// Cleanup temp directory
|
|
22
|
-
await rm(testDir, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('identifyTargets', () => {
|
|
26
|
-
it('should return empty array when no clean targets exist', async () => {
|
|
27
|
-
const targets = await cacheManager.identifyTargets();
|
|
28
|
-
expect(targets).toEqual([]);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('should identify dist directory', async () => {
|
|
32
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
33
|
-
const targets = await cacheManager.identifyTargets();
|
|
34
|
-
|
|
35
|
-
expect(targets.some(t => t.relativePath === 'dist')).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should identify .atomix directory', async () => {
|
|
39
|
-
await mkdir(join(testDir, '.atomix'), { recursive: true });
|
|
40
|
-
const targets = await cacheManager.identifyTargets();
|
|
41
|
-
|
|
42
|
-
expect(targets.some(t => t.relativePath === '.atomix')).toBe(true);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should identify node_modules/.cache with --all flag', async () => {
|
|
46
|
-
await mkdir(join(testDir, 'node_modules', '.cache'), { recursive: true });
|
|
47
|
-
|
|
48
|
-
// Without --all flag, node_modules should not be included
|
|
49
|
-
const targetsDefault = await cacheManager.identifyTargets();
|
|
50
|
-
// Default targets don't include node_modules at all
|
|
51
|
-
expect(targetsDefault.some(t => t.relativePath === 'node_modules')).toBe(false);
|
|
52
|
-
|
|
53
|
-
// With --all flag, node_modules should be included
|
|
54
|
-
const targetsAll = await cacheManager.identifyTargets({ all: true });
|
|
55
|
-
expect(targetsAll.some(t => t.relativePath === 'node_modules')).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should only identify cache directories with --cache flag', async () => {
|
|
59
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
60
|
-
await mkdir(join(testDir, '.atomix'), { recursive: true });
|
|
61
|
-
await mkdir(join(testDir, 'node_modules', '.cache'), { recursive: true });
|
|
62
|
-
|
|
63
|
-
const targets = await cacheManager.identifyTargets({ cache: true });
|
|
64
|
-
|
|
65
|
-
// Should include .atomix and node_modules/.cache but not log files
|
|
66
|
-
expect(targets.some(t => t.relativePath === '.atomix')).toBe(true);
|
|
67
|
-
expect(targets.some(t => t.relativePath.includes('node_modules'))).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('getPathType', () => {
|
|
72
|
-
it('should identify directory type', async () => {
|
|
73
|
-
await mkdir(join(testDir, 'test-dir'), { recursive: true });
|
|
74
|
-
const type = await cacheManager.getPathType(join(testDir, 'test-dir'));
|
|
75
|
-
expect(type).toBe('directory');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should identify file type', async () => {
|
|
79
|
-
await writeFile(join(testDir, 'test.txt'), 'content');
|
|
80
|
-
const type = await cacheManager.getPathType(join(testDir, 'test.txt'));
|
|
81
|
-
expect(type).toBe('file');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should return unknown for non-existent path', async () => {
|
|
85
|
-
const type = await cacheManager.getPathType(join(testDir, 'nonexistent'));
|
|
86
|
-
expect(type).toBe('unknown');
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('isProtected', () => {
|
|
91
|
-
it('should protect JavaScript files', () => {
|
|
92
|
-
expect(cacheManager.isProtected('file.js')).toBe(true);
|
|
93
|
-
expect(cacheManager.isProtected('file.jsx')).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should protect TypeScript files', () => {
|
|
97
|
-
expect(cacheManager.isProtected('file.ts')).toBe(true);
|
|
98
|
-
expect(cacheManager.isProtected('file.tsx')).toBe(true);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should protect style files', () => {
|
|
102
|
-
expect(cacheManager.isProtected('file.scss')).toBe(true);
|
|
103
|
-
expect(cacheManager.isProtected('file.css')).toBe(true);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should allow deleting build artifacts', () => {
|
|
107
|
-
expect(cacheManager.isProtected('file.map')).toBe(false);
|
|
108
|
-
expect(cacheManager.isProtected('file.bundle')).toBe(false);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe('formatBytes', () => {
|
|
113
|
-
it('should format zero bytes', () => {
|
|
114
|
-
expect(cacheManager.formatBytes(0)).toBe('0 Bytes');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('should format bytes to KB', () => {
|
|
118
|
-
expect(cacheManager.formatBytes(1024)).toBe('1 KB');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('should format bytes to MB', () => {
|
|
122
|
-
expect(cacheManager.formatBytes(1048576)).toBe('1 MB');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should format large sizes', () => {
|
|
126
|
-
expect(cacheManager.formatBytes(1073741824)).toBe('1 GB');
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('calculateSize', () => {
|
|
131
|
-
it('should calculate total size of files', async () => {
|
|
132
|
-
// Create test files
|
|
133
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
134
|
-
await writeFile(join(testDir, 'dist', 'file1.txt'), 'a'.repeat(100));
|
|
135
|
-
await writeFile(join(testDir, 'dist', 'file2.txt'), 'b'.repeat(200));
|
|
136
|
-
|
|
137
|
-
const targets = await cacheManager.identifyTargets();
|
|
138
|
-
const size = await cacheManager.calculateSize(targets);
|
|
139
|
-
|
|
140
|
-
expect(size).toBeGreaterThan(0);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe('deletePath', () => {
|
|
145
|
-
it('should delete a directory in dry-run mode without actually deleting', async () => {
|
|
146
|
-
process.env.ATOMIX_DRY_RUN = 'true';
|
|
147
|
-
|
|
148
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
149
|
-
await writeFile(join(testDir, 'dist', 'test.txt'), 'content');
|
|
150
|
-
|
|
151
|
-
// Use base path as testDir since filesystem validates against cwd
|
|
152
|
-
const result = await cacheManager.deletePath(join(testDir, 'dist'), { skipValidation: true });
|
|
153
|
-
|
|
154
|
-
expect(result).toBe(true);
|
|
155
|
-
|
|
156
|
-
// Verify directory still exists
|
|
157
|
-
const exists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
158
|
-
expect(exists).toBe('directory');
|
|
159
|
-
|
|
160
|
-
delete process.env.ATOMIX_DRY_RUN;
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should actually delete a directory when not in dry-run mode', async () => {
|
|
164
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
165
|
-
await writeFile(join(testDir, 'dist', 'test.txt'), 'content');
|
|
166
|
-
|
|
167
|
-
const result = await cacheManager.deletePath(join(testDir, 'dist'), { skipValidation: true });
|
|
168
|
-
|
|
169
|
-
expect(result).toBe(true);
|
|
170
|
-
|
|
171
|
-
// Verify directory is deleted
|
|
172
|
-
const exists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
173
|
-
expect(exists).toBe('unknown');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should refuse to delete protected files', async () => {
|
|
177
|
-
await writeFile(join(testDir, 'source.js'), 'console.log("hi")');
|
|
178
|
-
|
|
179
|
-
await expect(
|
|
180
|
-
cacheManager.deletePath(join(testDir, 'source.js'), { skipValidation: true })
|
|
181
|
-
).rejects.toThrow('Cannot delete protected source file');
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
describe('displayDryRun', () => {
|
|
186
|
-
it('should show message when nothing to clean', () => {
|
|
187
|
-
// This is mainly a visual test, we just ensure it doesn't throw
|
|
188
|
-
expect(() => {
|
|
189
|
-
cacheManager.displayDryRun([]);
|
|
190
|
-
}).not.toThrow();
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should display list of files to be deleted', () => {
|
|
194
|
-
const targets = [
|
|
195
|
-
{ relativePath: 'dist', type: 'directory' },
|
|
196
|
-
{ relativePath: '.atomix', type: 'directory' }
|
|
197
|
-
];
|
|
198
|
-
|
|
199
|
-
expect(() => {
|
|
200
|
-
cacheManager.displayDryRun(targets);
|
|
201
|
-
}).not.toThrow();
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
describe('cleanAction', () => {
|
|
207
|
-
let testDir;
|
|
208
|
-
|
|
209
|
-
beforeEach(async () => {
|
|
210
|
-
testDir = await mkdtemp(join(tmpdir(), 'atomix-action-test-'));
|
|
211
|
-
process.chdir(testDir);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
afterEach(async () => {
|
|
215
|
-
await rm(testDir, { recursive: true, force: true });
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('should handle empty state gracefully', async () => {
|
|
219
|
-
const { cleanAction } = await import('../commands/clean.js');
|
|
220
|
-
|
|
221
|
-
// Should not throw when nothing to clean
|
|
222
|
-
await expect(cleanAction({})).resolves.not.toThrow();
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
it('should respect dry-run mode', async () => {
|
|
226
|
-
const { cleanAction } = await import('../commands/clean.js');
|
|
227
|
-
|
|
228
|
-
// Create test directory
|
|
229
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
230
|
-
|
|
231
|
-
// Run in dry-run mode
|
|
232
|
-
process.env.ATOMIX_DRY_RUN = 'true';
|
|
233
|
-
await cleanAction({ dryRun: true });
|
|
234
|
-
|
|
235
|
-
// Verify dist still exists
|
|
236
|
-
const exists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
237
|
-
expect(exists).toBe('directory');
|
|
238
|
-
|
|
239
|
-
delete process.env.ATOMIX_DRY_RUN;
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('should clean created directories', async () => {
|
|
243
|
-
const { cleanAction } = await import('../commands/clean.js');
|
|
244
|
-
|
|
245
|
-
// Create test directories
|
|
246
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
247
|
-
await mkdir(join(testDir, '.atomix'), { recursive: true });
|
|
248
|
-
|
|
249
|
-
// Run clean
|
|
250
|
-
await cleanAction({});
|
|
251
|
-
|
|
252
|
-
// Verify directories are deleted
|
|
253
|
-
const distExists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
254
|
-
const atomixExists = await cacheManager.getPathType(join(testDir, '.atomix'));
|
|
255
|
-
|
|
256
|
-
expect(distExists).toBe('unknown');
|
|
257
|
-
expect(atomixExists).toBe('unknown');
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('should preserve source files', async () => {
|
|
261
|
-
const { cleanAction } = await import('../commands/clean.js');
|
|
262
|
-
|
|
263
|
-
// Create mix of source and build files
|
|
264
|
-
await mkdir(join(testDir, 'dist'), { recursive: true });
|
|
265
|
-
await writeFile(join(testDir, 'src.js'), 'console.log("source")');
|
|
266
|
-
|
|
267
|
-
// Run clean
|
|
268
|
-
await cleanAction({});
|
|
269
|
-
|
|
270
|
-
// Verify source file preserved
|
|
271
|
-
const srcExists = await cacheManager.getPathType(join(testDir, 'src.js'));
|
|
272
|
-
expect(srcExists).toBe('file');
|
|
273
|
-
|
|
274
|
-
// Verify build deleted
|
|
275
|
-
const distExists = await cacheManager.getPathType(join(testDir, 'dist'));
|
|
276
|
-
expect(distExists).toBe('unknown');
|
|
277
|
-
});
|
|
278
|
-
});
|
|
@@ -1,332 +0,0 @@
|
|
|
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
|
-
});
|