gh-here 3.0.2 โ 3.1.0
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/.env +0 -0
- package/.playwright-mcp/fixed-alignment.png +0 -0
- package/.playwright-mcp/fixed-layout.png +0 -0
- package/.playwright-mcp/gh-here-home-header-table.png +0 -0
- package/.playwright-mcp/gh-here-home.png +0 -0
- package/.playwright-mcp/line-selection-multiline.png +0 -0
- package/.playwright-mcp/line-selection-test-after.png +0 -0
- package/.playwright-mcp/line-selection-test-before.png +0 -0
- package/.playwright-mcp/page-2026-01-03T17-58-21-336Z.png +0 -0
- package/lib/constants.js +25 -15
- package/lib/content-search.js +212 -0
- package/lib/error-handler.js +39 -28
- package/lib/file-utils.js +438 -287
- package/lib/git.js +10 -54
- package/lib/gitignore.js +70 -41
- package/lib/renderers.js +15 -19
- package/lib/server.js +70 -193
- package/lib/symbol-parser.js +600 -0
- package/package.json +1 -1
- package/public/app.js +207 -73
- package/public/js/constants.js +50 -34
- package/public/js/content-search-handler.js +551 -0
- package/public/js/file-viewer.js +437 -0
- package/public/js/focus-mode.js +280 -0
- package/public/js/inline-search.js +659 -0
- package/public/js/modal-manager.js +14 -28
- package/public/js/navigation.js +5 -0
- package/public/js/symbol-outline.js +454 -0
- package/public/js/utils.js +152 -94
- package/public/styles.css +2049 -296
- package/.claude/settings.local.json +0 -30
- package/SAMPLE.md +0 -287
- package/lib/validation.js +0 -77
- package/public/app.js.backup +0 -1902
- package/public/js/draft-manager.js +0 -36
- package/public/js/editor-manager.js +0 -159
- package/test.js +0 -138
- package/tests/draftManager.test.js +0 -241
- package/tests/fileTypeDetection.test.js +0 -111
- package/tests/httpService.test.js +0 -268
- package/tests/languageDetection.test.js +0 -145
- package/tests/pathUtils.test.js +0 -136
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
// Tests for HTTP Service abstraction
|
|
2
|
-
// Run with: node tests/httpService.test.js
|
|
3
|
-
|
|
4
|
-
// Simple mock implementation for jest.fn()
|
|
5
|
-
function jest_fn() {
|
|
6
|
-
const calls = [];
|
|
7
|
-
const mockFn = (...args) => {
|
|
8
|
-
calls.push(args);
|
|
9
|
-
return mockFn._returnValue;
|
|
10
|
-
};
|
|
11
|
-
mockFn.mock = { calls };
|
|
12
|
-
mockFn.mockResolvedValue = (value) => {
|
|
13
|
-
mockFn._returnValue = Promise.resolve(value);
|
|
14
|
-
return mockFn;
|
|
15
|
-
};
|
|
16
|
-
return mockFn;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Replace jest with our simple implementation
|
|
20
|
-
const jest = { fn: jest_fn };
|
|
21
|
-
|
|
22
|
-
// Mock fetch for testing
|
|
23
|
-
class MockResponse {
|
|
24
|
-
constructor(data, options = {}) {
|
|
25
|
-
this._data = data;
|
|
26
|
-
this.ok = options.ok !== false;
|
|
27
|
-
this.status = options.status || (this.ok ? 200 : 500);
|
|
28
|
-
this.statusText = options.statusText || (this.ok ? 'OK' : 'Internal Server Error');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async json() {
|
|
32
|
-
return this._data;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async text() {
|
|
36
|
-
return typeof this._data === 'string' ? this._data : JSON.stringify(this._data);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Mock fetch implementation
|
|
41
|
-
function createMockFetch(responseData, options = {}) {
|
|
42
|
-
return jest.fn().mockResolvedValue(new MockResponse(responseData, options));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// HTTP Service abstraction - pure functions, easily testable
|
|
46
|
-
function createHttpService(fetchFn = fetch) {
|
|
47
|
-
return {
|
|
48
|
-
// File operations
|
|
49
|
-
async getFileContent(filePath) {
|
|
50
|
-
const response = await fetchFn(`/api/file-content?path=${encodeURIComponent(filePath)}`);
|
|
51
|
-
if (!response.ok) {
|
|
52
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
53
|
-
}
|
|
54
|
-
return response.text();
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
async saveFile(filePath, content) {
|
|
58
|
-
const response = await fetchFn('/api/save-file', {
|
|
59
|
-
method: 'POST',
|
|
60
|
-
headers: { 'Content-Type': 'application/json' },
|
|
61
|
-
body: JSON.stringify({ path: filePath, content })
|
|
62
|
-
});
|
|
63
|
-
return response.json();
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
async createFile(path, filename) {
|
|
67
|
-
const response = await fetchFn('/api/create-file', {
|
|
68
|
-
method: 'POST',
|
|
69
|
-
headers: { 'Content-Type': 'application/json' },
|
|
70
|
-
body: JSON.stringify({ path, filename })
|
|
71
|
-
});
|
|
72
|
-
return response.json();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
async deleteFile(path) {
|
|
76
|
-
const response = await fetchFn('/api/delete', {
|
|
77
|
-
method: 'POST',
|
|
78
|
-
headers: { 'Content-Type': 'application/json' },
|
|
79
|
-
body: JSON.stringify({ path })
|
|
80
|
-
});
|
|
81
|
-
return response.json();
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
async renameFile(oldPath, newPath) {
|
|
85
|
-
const response = await fetchFn('/api/rename', {
|
|
86
|
-
method: 'POST',
|
|
87
|
-
headers: { 'Content-Type': 'application/json' },
|
|
88
|
-
body: JSON.stringify({ oldPath, newPath })
|
|
89
|
-
});
|
|
90
|
-
return response.json();
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
// Folder operations
|
|
94
|
-
async createFolder(path, foldername) {
|
|
95
|
-
const response = await fetchFn('/api/create-folder', {
|
|
96
|
-
method: 'POST',
|
|
97
|
-
headers: { 'Content-Type': 'application/json' },
|
|
98
|
-
body: JSON.stringify({ path, foldername })
|
|
99
|
-
});
|
|
100
|
-
return response.json();
|
|
101
|
-
},
|
|
102
|
-
|
|
103
|
-
// Git operations
|
|
104
|
-
async getGitStatus(currentPath) {
|
|
105
|
-
const response = await fetchFn(`/api/git-status?currentPath=${encodeURIComponent(currentPath)}`);
|
|
106
|
-
return response.json();
|
|
107
|
-
},
|
|
108
|
-
|
|
109
|
-
async getGitDiff(filePath) {
|
|
110
|
-
const response = await fetchFn(`/api/git-diff?path=${encodeURIComponent(filePath)}`);
|
|
111
|
-
return response.json();
|
|
112
|
-
},
|
|
113
|
-
|
|
114
|
-
async commitSelectedFiles(files, message) {
|
|
115
|
-
const response = await fetchFn('/api/git-commit-selected', {
|
|
116
|
-
method: 'POST',
|
|
117
|
-
headers: { 'Content-Type': 'application/json' },
|
|
118
|
-
body: JSON.stringify({ files, message })
|
|
119
|
-
});
|
|
120
|
-
return response.json();
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Simple test framework (since we don't have a real test runner)
|
|
126
|
-
function test(name, fn) {
|
|
127
|
-
try {
|
|
128
|
-
fn();
|
|
129
|
-
console.log(`โ
${name}`);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.log(`โ ${name}: ${error.message}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function assertEqual(actual, expected, message = '') {
|
|
136
|
-
if (actual !== expected) {
|
|
137
|
-
throw new Error(`Expected "${expected}" but got "${actual}". ${message}`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function assertDeepEqual(actual, expected, message = '') {
|
|
142
|
-
const actualStr = JSON.stringify(actual);
|
|
143
|
-
const expectedStr = JSON.stringify(expected);
|
|
144
|
-
if (actualStr !== expectedStr) {
|
|
145
|
-
throw new Error(`Expected ${expectedStr} but got ${actualStr}. ${message}`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Test cases
|
|
150
|
-
console.log('๐งช Testing HTTP Service\n');
|
|
151
|
-
|
|
152
|
-
test('gets file content correctly', async () => {
|
|
153
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse('file content'));
|
|
154
|
-
const httpService = createHttpService(mockFetch);
|
|
155
|
-
|
|
156
|
-
const content = await httpService.getFileContent('src/app.js');
|
|
157
|
-
assertEqual(content, 'file content');
|
|
158
|
-
assertEqual(mockFetch.mock.calls[0][0], '/api/file-content?path=src%2Fapp.js');
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test('saves file with correct parameters', async () => {
|
|
162
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ success: true }));
|
|
163
|
-
const httpService = createHttpService(mockFetch);
|
|
164
|
-
|
|
165
|
-
const result = await httpService.saveFile('src/app.js', 'new content');
|
|
166
|
-
assertDeepEqual(result, { success: true });
|
|
167
|
-
|
|
168
|
-
const [url, options] = mockFetch.mock.calls[0];
|
|
169
|
-
assertEqual(url, '/api/save-file');
|
|
170
|
-
assertEqual(options.method, 'POST');
|
|
171
|
-
assertDeepEqual(JSON.parse(options.body), { path: 'src/app.js', content: 'new content' });
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test('creates file with correct parameters', async () => {
|
|
175
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ success: true }));
|
|
176
|
-
const httpService = createHttpService(mockFetch);
|
|
177
|
-
|
|
178
|
-
const result = await httpService.createFile('src', 'newfile.js');
|
|
179
|
-
assertDeepEqual(result, { success: true });
|
|
180
|
-
|
|
181
|
-
const [url, options] = mockFetch.mock.calls[0];
|
|
182
|
-
assertEqual(url, '/api/create-file');
|
|
183
|
-
assertDeepEqual(JSON.parse(options.body), { path: 'src', filename: 'newfile.js' });
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test('deletes file with correct parameters', async () => {
|
|
187
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ success: true }));
|
|
188
|
-
const httpService = createHttpService(mockFetch);
|
|
189
|
-
|
|
190
|
-
const result = await httpService.deleteFile('src/oldfile.js');
|
|
191
|
-
assertDeepEqual(result, { success: true });
|
|
192
|
-
|
|
193
|
-
const [url, options] = mockFetch.mock.calls[0];
|
|
194
|
-
assertEqual(url, '/api/delete');
|
|
195
|
-
assertDeepEqual(JSON.parse(options.body), { path: 'src/oldfile.js' });
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test('renames file with correct parameters', async () => {
|
|
199
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ success: true }));
|
|
200
|
-
const httpService = createHttpService(mockFetch);
|
|
201
|
-
|
|
202
|
-
const result = await httpService.renameFile('src/old.js', 'src/new.js');
|
|
203
|
-
assertDeepEqual(result, { success: true });
|
|
204
|
-
|
|
205
|
-
const [url, options] = mockFetch.mock.calls[0];
|
|
206
|
-
assertEqual(url, '/api/rename');
|
|
207
|
-
assertDeepEqual(JSON.parse(options.body), { oldPath: 'src/old.js', newPath: 'src/new.js' });
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test('creates folder with correct parameters', async () => {
|
|
211
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ success: true }));
|
|
212
|
-
const httpService = createHttpService(mockFetch);
|
|
213
|
-
|
|
214
|
-
const result = await httpService.createFolder('src', 'components');
|
|
215
|
-
assertDeepEqual(result, { success: true });
|
|
216
|
-
|
|
217
|
-
const [url, options] = mockFetch.mock.calls[0];
|
|
218
|
-
assertEqual(url, '/api/create-folder');
|
|
219
|
-
assertDeepEqual(JSON.parse(options.body), { path: 'src', foldername: 'components' });
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test('gets git status with correct parameters', async () => {
|
|
223
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ files: [] }));
|
|
224
|
-
const httpService = createHttpService(mockFetch);
|
|
225
|
-
|
|
226
|
-
const result = await httpService.getGitStatus('src');
|
|
227
|
-
assertDeepEqual(result, { files: [] });
|
|
228
|
-
|
|
229
|
-
assertEqual(mockFetch.mock.calls[0][0], '/api/git-status?currentPath=src');
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
test('gets git diff with correct parameters', async () => {
|
|
233
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ diff: 'diff content' }));
|
|
234
|
-
const httpService = createHttpService(mockFetch);
|
|
235
|
-
|
|
236
|
-
const result = await httpService.getGitDiff('src/app.js');
|
|
237
|
-
assertDeepEqual(result, { diff: 'diff content' });
|
|
238
|
-
|
|
239
|
-
assertEqual(mockFetch.mock.calls[0][0], '/api/git-diff?path=src%2Fapp.js');
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('commits selected files with correct parameters', async () => {
|
|
243
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse({ success: true }));
|
|
244
|
-
const httpService = createHttpService(mockFetch);
|
|
245
|
-
|
|
246
|
-
const files = ['src/app.js', 'src/utils.js'];
|
|
247
|
-
const message = 'Fix bugs';
|
|
248
|
-
const result = await httpService.commitSelectedFiles(files, message);
|
|
249
|
-
assertDeepEqual(result, { success: true });
|
|
250
|
-
|
|
251
|
-
const [url, options] = mockFetch.mock.calls[0];
|
|
252
|
-
assertEqual(url, '/api/git-commit-selected');
|
|
253
|
-
assertDeepEqual(JSON.parse(options.body), { files, message });
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
test('handles HTTP errors correctly', async () => {
|
|
257
|
-
const mockFetch = jest.fn().mockResolvedValue(new MockResponse('Not found', { ok: false, status: 404, statusText: 'Not Found' }));
|
|
258
|
-
const httpService = createHttpService(mockFetch);
|
|
259
|
-
|
|
260
|
-
try {
|
|
261
|
-
await httpService.getFileContent('nonexistent.js');
|
|
262
|
-
throw new Error('Should have thrown an error');
|
|
263
|
-
} catch (error) {
|
|
264
|
-
assertEqual(error.message, 'HTTP 404: Not Found');
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
console.log('\n๐ HTTP Service tests complete!');
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// Simple unit tests for language detection
|
|
2
|
-
// Run with: node tests/languageDetection.test.js
|
|
3
|
-
|
|
4
|
-
// Import the function (in a real setup, you'd use proper modules)
|
|
5
|
-
// For now, we'll copy the function to test it standalone
|
|
6
|
-
|
|
7
|
-
function getLanguageFromExtension(filename) {
|
|
8
|
-
const ext = filename.split('.').pop().toLowerCase();
|
|
9
|
-
const languageMap = {
|
|
10
|
-
// JavaScript family
|
|
11
|
-
'js': 'javascript',
|
|
12
|
-
'mjs': 'javascript',
|
|
13
|
-
'jsx': 'javascript',
|
|
14
|
-
'ts': 'typescript',
|
|
15
|
-
'tsx': 'typescript',
|
|
16
|
-
|
|
17
|
-
// Web languages
|
|
18
|
-
'html': 'html',
|
|
19
|
-
'htm': 'html',
|
|
20
|
-
'css': 'css',
|
|
21
|
-
'scss': 'scss',
|
|
22
|
-
'sass': 'sass',
|
|
23
|
-
'less': 'less',
|
|
24
|
-
|
|
25
|
-
// Data formats
|
|
26
|
-
'json': 'json',
|
|
27
|
-
'xml': 'xml',
|
|
28
|
-
'yaml': 'yaml',
|
|
29
|
-
'yml': 'yaml',
|
|
30
|
-
|
|
31
|
-
// Programming languages
|
|
32
|
-
'py': 'python',
|
|
33
|
-
'java': 'java',
|
|
34
|
-
'go': 'go',
|
|
35
|
-
'rs': 'rust',
|
|
36
|
-
'php': 'php',
|
|
37
|
-
'rb': 'ruby',
|
|
38
|
-
'swift': 'swift',
|
|
39
|
-
'kt': 'kotlin',
|
|
40
|
-
'dart': 'dart',
|
|
41
|
-
|
|
42
|
-
// Systems languages
|
|
43
|
-
'c': 'c',
|
|
44
|
-
'cpp': 'cpp',
|
|
45
|
-
'cc': 'cpp',
|
|
46
|
-
'cxx': 'cpp',
|
|
47
|
-
'h': 'c',
|
|
48
|
-
'hpp': 'cpp',
|
|
49
|
-
|
|
50
|
-
// Shell and scripts
|
|
51
|
-
'sh': 'shell',
|
|
52
|
-
'bash': 'shell',
|
|
53
|
-
'zsh': 'shell',
|
|
54
|
-
'fish': 'shell',
|
|
55
|
-
'ps1': 'powershell',
|
|
56
|
-
|
|
57
|
-
// Other languages
|
|
58
|
-
'sql': 'sql',
|
|
59
|
-
'r': 'r',
|
|
60
|
-
'scala': 'scala',
|
|
61
|
-
'clj': 'clojure',
|
|
62
|
-
'lua': 'lua',
|
|
63
|
-
'pl': 'perl',
|
|
64
|
-
'groovy': 'groovy',
|
|
65
|
-
|
|
66
|
-
// Config and text
|
|
67
|
-
'md': 'markdown',
|
|
68
|
-
'txt': 'plaintext',
|
|
69
|
-
'log': 'plaintext'
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// Special filename handling
|
|
73
|
-
const basename = filename.toLowerCase();
|
|
74
|
-
if (basename === 'dockerfile' || basename.startsWith('dockerfile.')) return 'dockerfile';
|
|
75
|
-
if (basename === 'makefile') return 'makefile';
|
|
76
|
-
if (basename.startsWith('.env')) return 'dotenv';
|
|
77
|
-
if (basename === 'package.json' || basename === 'composer.json') return 'json';
|
|
78
|
-
|
|
79
|
-
return languageMap[ext] || 'plaintext';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Simple test framework
|
|
83
|
-
function test(name, fn) {
|
|
84
|
-
try {
|
|
85
|
-
fn();
|
|
86
|
-
console.log(`โ
${name}`);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.log(`โ ${name}: ${error.message}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function assertEqual(actual, expected, message = '') {
|
|
93
|
-
if (actual !== expected) {
|
|
94
|
-
throw new Error(`Expected "${expected}" but got "${actual}". ${message}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Test cases
|
|
99
|
-
console.log('๐งช Testing Language Detection Function\n');
|
|
100
|
-
|
|
101
|
-
test('detects JavaScript files', () => {
|
|
102
|
-
assertEqual(getLanguageFromExtension('app.js'), 'javascript');
|
|
103
|
-
assertEqual(getLanguageFromExtension('module.mjs'), 'javascript');
|
|
104
|
-
assertEqual(getLanguageFromExtension('Component.jsx'), 'javascript');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test('detects TypeScript files', () => {
|
|
108
|
-
assertEqual(getLanguageFromExtension('main.ts'), 'typescript');
|
|
109
|
-
assertEqual(getLanguageFromExtension('Component.tsx'), 'typescript');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
test('detects web languages', () => {
|
|
113
|
-
assertEqual(getLanguageFromExtension('index.html'), 'html');
|
|
114
|
-
assertEqual(getLanguageFromExtension('styles.css'), 'css');
|
|
115
|
-
assertEqual(getLanguageFromExtension('styles.scss'), 'scss');
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('detects programming languages', () => {
|
|
119
|
-
assertEqual(getLanguageFromExtension('script.py'), 'python');
|
|
120
|
-
assertEqual(getLanguageFromExtension('Main.java'), 'java');
|
|
121
|
-
assertEqual(getLanguageFromExtension('main.go'), 'go');
|
|
122
|
-
assertEqual(getLanguageFromExtension('lib.rs'), 'rust');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test('detects special filenames', () => {
|
|
126
|
-
assertEqual(getLanguageFromExtension('Dockerfile'), 'dockerfile');
|
|
127
|
-
assertEqual(getLanguageFromExtension('dockerfile.prod'), 'dockerfile');
|
|
128
|
-
assertEqual(getLanguageFromExtension('Makefile'), 'makefile');
|
|
129
|
-
assertEqual(getLanguageFromExtension('.env'), 'dotenv');
|
|
130
|
-
assertEqual(getLanguageFromExtension('.env.local'), 'dotenv');
|
|
131
|
-
assertEqual(getLanguageFromExtension('package.json'), 'json');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test('defaults to plaintext for unknown extensions', () => {
|
|
135
|
-
assertEqual(getLanguageFromExtension('file.unknown'), 'plaintext');
|
|
136
|
-
assertEqual(getLanguageFromExtension('README'), 'plaintext');
|
|
137
|
-
assertEqual(getLanguageFromExtension('file.xyz'), 'plaintext');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test('handles files without extensions', () => {
|
|
141
|
-
assertEqual(getLanguageFromExtension('README'), 'plaintext');
|
|
142
|
-
assertEqual(getLanguageFromExtension('LICENSE'), 'plaintext');
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
console.log('\n๐ Language detection tests complete!');
|
package/tests/pathUtils.test.js
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
// Tests for Path Utilities
|
|
2
|
-
// Run with: node tests/pathUtils.test.js
|
|
3
|
-
|
|
4
|
-
// Path utility functions extracted for testability
|
|
5
|
-
const PathUtils = {
|
|
6
|
-
// Extract current path from URL parameters
|
|
7
|
-
getCurrentPath() {
|
|
8
|
-
const currentUrl = new URL(window.location.href);
|
|
9
|
-
return currentUrl.searchParams.get('path') || '';
|
|
10
|
-
},
|
|
11
|
-
|
|
12
|
-
// Navigate to parent directory
|
|
13
|
-
getParentPath(currentPath) {
|
|
14
|
-
if (!currentPath || currentPath === '') {
|
|
15
|
-
return null; // Already at root
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const pathParts = currentPath.split('/').filter(p => p);
|
|
19
|
-
if (pathParts.length === 0) {
|
|
20
|
-
return null; // Already at root
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
pathParts.pop();
|
|
24
|
-
return pathParts.join('/');
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
// Build file path from directory and filename
|
|
28
|
-
buildFilePath(currentPath, filename) {
|
|
29
|
-
return currentPath ? `${currentPath}/${filename}` : filename;
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
// Get filename from full path
|
|
33
|
-
getFileName(filePath) {
|
|
34
|
-
return filePath.split('/').pop() || 'file.txt';
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
// Build URL with encoded path parameter
|
|
38
|
-
buildPathUrl(basePath, targetPath) {
|
|
39
|
-
return targetPath ? `${basePath}?path=${encodeURIComponent(targetPath)}` : basePath;
|
|
40
|
-
},
|
|
41
|
-
|
|
42
|
-
// Extract directory path from file path
|
|
43
|
-
getDirectoryPath(filePath) {
|
|
44
|
-
const parts = filePath.split('/').filter(p => p);
|
|
45
|
-
if (parts.length <= 1) return '';
|
|
46
|
-
return parts.slice(0, -1).join('/');
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Simple test framework
|
|
51
|
-
function test(name, fn) {
|
|
52
|
-
try {
|
|
53
|
-
fn();
|
|
54
|
-
console.log(`โ
${name}`);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
console.log(`โ ${name}: ${error.message}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function assertEqual(actual, expected, message = '') {
|
|
61
|
-
if (actual !== expected) {
|
|
62
|
-
throw new Error(`Expected "${expected}" but got "${actual}". ${message}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function assertNull(actual, message = '') {
|
|
67
|
-
if (actual !== null) {
|
|
68
|
-
throw new Error(`Expected null but got "${actual}". ${message}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Test cases
|
|
73
|
-
console.log('๐งช Testing Path Utilities\n');
|
|
74
|
-
|
|
75
|
-
test('gets parent path correctly', () => {
|
|
76
|
-
assertEqual(PathUtils.getParentPath('src/components/App.js'), 'src/components');
|
|
77
|
-
assertEqual(PathUtils.getParentPath('src/App.js'), 'src');
|
|
78
|
-
assertEqual(PathUtils.getParentPath('App.js'), '');
|
|
79
|
-
assertNull(PathUtils.getParentPath(''));
|
|
80
|
-
assertNull(PathUtils.getParentPath(null));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test('builds file paths correctly', () => {
|
|
84
|
-
assertEqual(PathUtils.buildFilePath('src/components', 'App.js'), 'src/components/App.js');
|
|
85
|
-
assertEqual(PathUtils.buildFilePath('src', 'App.js'), 'src/App.js');
|
|
86
|
-
assertEqual(PathUtils.buildFilePath('', 'App.js'), 'App.js');
|
|
87
|
-
assertEqual(PathUtils.buildFilePath(null, 'App.js'), 'App.js');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('gets filename from path correctly', () => {
|
|
91
|
-
assertEqual(PathUtils.getFileName('src/components/App.js'), 'App.js');
|
|
92
|
-
assertEqual(PathUtils.getFileName('App.js'), 'App.js');
|
|
93
|
-
assertEqual(PathUtils.getFileName(''), 'file.txt');
|
|
94
|
-
assertEqual(PathUtils.getFileName('src/components/'), 'file.txt');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('builds URLs with path parameters', () => {
|
|
98
|
-
assertEqual(PathUtils.buildPathUrl('/', 'src/components'), '/?path=src%2Fcomponents');
|
|
99
|
-
assertEqual(PathUtils.buildPathUrl('/new', 'src'), '/new?path=src');
|
|
100
|
-
assertEqual(PathUtils.buildPathUrl('/', ''), '/');
|
|
101
|
-
assertEqual(PathUtils.buildPathUrl('/new', null), '/new');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test('gets directory path from file path', () => {
|
|
105
|
-
assertEqual(PathUtils.getDirectoryPath('src/components/App.js'), 'src/components');
|
|
106
|
-
assertEqual(PathUtils.getDirectoryPath('src/App.js'), 'src');
|
|
107
|
-
assertEqual(PathUtils.getDirectoryPath('App.js'), '');
|
|
108
|
-
assertEqual(PathUtils.getDirectoryPath(''), '');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('handles git commit dialog scenarios correctly', () => {
|
|
112
|
-
// Test cases that match the commit dialog usage pattern
|
|
113
|
-
// where we extract directory from file.name
|
|
114
|
-
assertEqual(PathUtils.getDirectoryPath('src/components/Button.tsx'), 'src/components');
|
|
115
|
-
assertEqual(PathUtils.getDirectoryPath('lib/utils/helper.js'), 'lib/utils');
|
|
116
|
-
assertEqual(PathUtils.getDirectoryPath('tests/unit/test.js'), 'tests/unit');
|
|
117
|
-
assertEqual(PathUtils.getDirectoryPath('package.json'), ''); // Root level file
|
|
118
|
-
assertEqual(PathUtils.getDirectoryPath('docs/README.md'), 'docs');
|
|
119
|
-
|
|
120
|
-
// Edge cases from commit dialog
|
|
121
|
-
assertEqual(PathUtils.getDirectoryPath('very/deeply/nested/folder/structure/file.txt'), 'very/deeply/nested/folder/structure');
|
|
122
|
-
assertEqual(PathUtils.getDirectoryPath('single-level.txt'), '');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test('handles edge cases in parent path', () => {
|
|
126
|
-
assertEqual(PathUtils.getParentPath('a/b/c/d/e'), 'a/b/c/d');
|
|
127
|
-
assertEqual(PathUtils.getParentPath('single'), '');
|
|
128
|
-
assertNull(PathUtils.getParentPath(''));
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('handles edge cases in file path building', () => {
|
|
132
|
-
assertEqual(PathUtils.buildFilePath('a/b/c', 'file.txt'), 'a/b/c/file.txt');
|
|
133
|
-
assertEqual(PathUtils.buildFilePath('', 'README.md'), 'README.md');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
console.log('\n๐ Path utilities tests complete!');
|