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