create-ripple 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,192 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateProjectName, sanitizeDirectoryName, validateDirectoryPath } from '../../src/lib/validation.js';
3
+
4
+ describe('validateProjectName', () => {
5
+ it('should validate correct project names', () => {
6
+ const validNames = [
7
+ 'my-app',
8
+ 'my.app',
9
+ 'my_app',
10
+ 'myapp',
11
+ 'my-awesome-app',
12
+ 'app123',
13
+ 'a',
14
+ 'a'.repeat(214) // max length
15
+ ];
16
+
17
+ validNames.forEach(name => {
18
+ const result = validateProjectName(name);
19
+ expect(result.valid).toBe(true);
20
+ expect(result.message).toBe('');
21
+ });
22
+ });
23
+
24
+ it('should reject invalid project names', () => {
25
+ const invalidCases = [
26
+ { name: '', expectedMessage: 'Project name cannot be empty' },
27
+ { name: ' ', expectedMessage: 'Project name cannot be empty' },
28
+ { name: null, expectedMessage: 'Project name is required' },
29
+ { name: undefined, expectedMessage: 'Project name is required' },
30
+ { name: 123, expectedMessage: 'Project name is required' },
31
+ { name: 'a'.repeat(215), expectedMessage: 'Project name must be less than 214 characters' }
32
+ ];
33
+
34
+ invalidCases.forEach(({ name, expectedMessage }) => {
35
+ const result = validateProjectName(name);
36
+ expect(result.valid).toBe(false);
37
+ expect(result.message).toBe(expectedMessage);
38
+ });
39
+ });
40
+
41
+ it('should reject names with invalid characters', () => {
42
+ const invalidNames = [
43
+ 'My-App', // uppercase
44
+ 'my app', // space
45
+ 'my@app', // special character
46
+ 'my/app', // slash
47
+ 'my\\app', // backslash
48
+ 'my:app', // colon
49
+ 'my*app', // asterisk
50
+ 'my?app', // question mark
51
+ 'my"app', // quote
52
+ 'my<app', // less than
53
+ 'my>app' // greater than
54
+ ];
55
+
56
+ invalidNames.forEach(name => {
57
+ const result = validateProjectName(name);
58
+ expect(result.valid).toBe(false);
59
+ expect(result.message).toBe(
60
+ 'Project name can only contain lowercase letters, numbers, hyphens, dots, and underscores'
61
+ );
62
+ });
63
+ });
64
+
65
+ it('should reject names starting with dot or underscore', () => {
66
+ const invalidNames = ['.my-app', '_my-app'];
67
+
68
+ invalidNames.forEach(name => {
69
+ const result = validateProjectName(name);
70
+ expect(result.valid).toBe(false);
71
+ expect(result.message).toBe('Project name cannot start with a dot or underscore');
72
+ });
73
+ });
74
+
75
+ it('should reject names ending with dot', () => {
76
+ const result = validateProjectName('my-app.');
77
+ expect(result.valid).toBe(false);
78
+ expect(result.message).toBe('Project name cannot end with a dot');
79
+ });
80
+
81
+ it('should reject names with consecutive dots', () => {
82
+ const result = validateProjectName('my..app');
83
+ expect(result.valid).toBe(false);
84
+ expect(result.message).toBe('Project name cannot contain consecutive dots');
85
+ });
86
+
87
+ it('should reject reserved names', () => {
88
+ const reservedNames = [
89
+ 'node_modules',
90
+ 'favicon.ico',
91
+ 'con',
92
+ 'prn',
93
+ 'aux',
94
+ 'nul',
95
+ 'com1',
96
+ 'com2',
97
+ 'lpt1',
98
+ 'lpt9'
99
+ ];
100
+
101
+ reservedNames.forEach(name => {
102
+ const result = validateProjectName(name);
103
+ expect(result.valid).toBe(false);
104
+ expect(result.message).toBe(`"${name}" is a reserved name and cannot be used`);
105
+ });
106
+ });
107
+
108
+ it('should handle case insensitive reserved names', () => {
109
+ const result = validateProjectName('con'); // use lowercase since validation requires lowercase
110
+ expect(result.valid).toBe(false);
111
+ expect(result.message).toBe('"con" is a reserved name and cannot be used');
112
+ });
113
+ });
114
+
115
+ describe('sanitizeDirectoryName', () => {
116
+ it('should sanitize directory names correctly', () => {
117
+ const testCases = [
118
+ { input: 'My App', expected: 'my-app' },
119
+ { input: 'my@app#name', expected: 'my-app-name' },
120
+ { input: '---my-app---', expected: 'my-app' },
121
+ { input: 'my___app', expected: 'my-app' },
122
+ { input: 'MY-APP', expected: 'my-app' },
123
+ { input: 'app123!@#', expected: 'app123' },
124
+ { input: ' spaces ', expected: 'spaces' },
125
+ { input: 'special$%^chars', expected: 'special-chars' }
126
+ ];
127
+
128
+ testCases.forEach(({ input, expected }) => {
129
+ const result = sanitizeDirectoryName(input);
130
+ expect(result).toBe(expected);
131
+ });
132
+ });
133
+
134
+ it('should handle edge cases', () => {
135
+ expect(sanitizeDirectoryName('')).toBe('');
136
+ expect(sanitizeDirectoryName('---')).toBe('');
137
+ expect(sanitizeDirectoryName('123')).toBe('123');
138
+ expect(sanitizeDirectoryName('a')).toBe('a');
139
+ });
140
+ });
141
+
142
+ describe('validateDirectoryPath', () => {
143
+ it('should validate correct directory paths', () => {
144
+ const validPaths = [
145
+ 'my-app',
146
+ './my-app',
147
+ '../my-app',
148
+ 'path/to/my-app',
149
+ '/home/user/projects/my-app'
150
+ ];
151
+
152
+ validPaths.forEach(path => {
153
+ const result = validateDirectoryPath(path);
154
+ expect(result.valid).toBe(true);
155
+ expect(result.message).toBe('');
156
+ });
157
+ });
158
+
159
+ it('should reject invalid directory paths', () => {
160
+ const invalidCases = [
161
+ { path: '', expectedMessage: 'Directory path is required' },
162
+ { path: null, expectedMessage: 'Directory path is required' },
163
+ { path: undefined, expectedMessage: 'Directory path is required' },
164
+ { path: 123, expectedMessage: 'Directory path is required' },
165
+ { path: '/', expectedMessage: 'Cannot create project in root directory' }
166
+ ];
167
+
168
+ invalidCases.forEach(({ path, expectedMessage }) => {
169
+ const result = validateDirectoryPath(path);
170
+ expect(result.valid).toBe(false);
171
+ expect(result.message).toBe(expectedMessage);
172
+ });
173
+ });
174
+
175
+ it('should reject paths with invalid characters', () => {
176
+ const invalidPaths = [
177
+ 'my<app',
178
+ 'my>app',
179
+ 'my:app',
180
+ 'my"app',
181
+ 'my|app',
182
+ 'my?app',
183
+ 'my*app'
184
+ ];
185
+
186
+ invalidPaths.forEach(path => {
187
+ const result = validateDirectoryPath(path);
188
+ expect(result.valid).toBe(false);
189
+ expect(result.message).toBe('Directory path contains invalid characters');
190
+ });
191
+ });
192
+ });
@@ -0,0 +1,22 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ['tests/**/*.test.js'],
6
+ environment: 'node',
7
+ globals: true,
8
+ coverage: {
9
+ provider: 'v8',
10
+ reporter: ['text', 'json', 'html'],
11
+ exclude: [
12
+ 'node_modules/',
13
+ 'tests/',
14
+ 'coverage/',
15
+ '**/*.test.js',
16
+ '**/*.config.js'
17
+ ]
18
+ },
19
+ testTimeout: 60000, // 60 seconds for integration tests
20
+ hookTimeout: 10000 // 10 seconds for setup/teardown
21
+ }
22
+ });