Asper Header  1.0.14
The header injector extension
Loading...
Searching...
No Matches
extension.test.ts
Go to the documentation of this file.
1
27import * as assert from 'assert';
28import * as vscode from 'vscode';
29import * as path from 'path';
30import * as fs from 'fs/promises';
31import { moduleName } from '../constants';
32import { activate, deactivate } from '../extension';
33
41suite('Extension Integration Test Suite', () => {
43 let testDocument: vscode.TextDocument | undefined;
45 let testEditor: vscode.TextEditor | undefined;
47 let tempDir: string;
49 let extensionContext: vscode.ExtensionContext;
50
58 suiteSetup(async () => {
59 // Create temporary directory for test files
60 tempDir = await fs.mkdtemp(path.join(require('os').tmpdir(), 'extension-test-'));
61
62 // Try to get extension context (may not be available in test environment)
63 const possibleIds = ['asperguide.asperheader', 'henryletellierfr.asperheader', 'asperheader'];
64 let extension: vscode.Extension<any> | undefined;
65
66 for (const id of possibleIds) {
67 extension = vscode.extensions.getExtension(id);
68 if (extension) {
69 break;
70 }
71 }
72
73 if (extension) {
74 try {
75 await extension.activate();
76 extensionContext = extension.exports?.context || {
77 extensionPath: extension.extensionPath,
78 subscriptions: [],
79 workspaceState: {
80 get: () => undefined,
81 update: () => Promise.resolve()
82 },
83 globalState: {
84 get: () => undefined,
85 update: () => Promise.resolve(),
86 setKeysForSync: () => { }
87 }
88 } as any;
89 } catch (error) {
90 // Extension activation might fail in test environment
91 console.log('Extension activation failed in test environment:', error);
92 }
93 } else {
94 // Create mock context for test environment
95 extensionContext = {
96 extensionPath: path.resolve(__dirname, '..', '..'),
97 subscriptions: [],
98 workspaceState: {
99 get: () => undefined,
100 update: () => Promise.resolve()
101 },
102 globalState: {
103 get: () => undefined,
104 update: () => Promise.resolve(),
105 setKeysForSync: () => { }
106 }
107 } as any;
108 }
109 });
110
118 suiteTeardown(async () => {
119 // Cleanup temporary files
120 if (tempDir) {
121 await fs.rmdir(tempDir, { recursive: true }).catch(() => { });
122 }
123
124 // Close any open test documents
125 if (testDocument) {
126 await vscode.window.showTextDocument(testDocument);
127 await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
128 }
129 });
130
138 setup(async () => {
139 // Reset test state before each test
140 testDocument = undefined;
141 testEditor = undefined;
142 });
143
151 teardown(async () => {
152 // Cleanup after each test
153 if (testEditor) {
154 await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
155 }
156 });
157
165 suite('Extension Lifecycle', () => {
170 test('should activate successfully', async () => {
171 // Try multiple possible extension identifiers
172 const possibleIds = [
173 'asperguide.asperheader',
174 'henryletellierfr.asperheader',
175 'asperheader'
176 ];
177
178 let extension: vscode.Extension<any> | undefined;
179 for (const id of possibleIds) {
180 extension = vscode.extensions.getExtension(id);
181 if (extension) {
182 break;
183 }
184 }
185
186 // If no extension found by ID, check if we're running in development
187 if (!extension) {
188 // In test environment, the extension might not be formally "installed"
189 // Check if our activate function exists and can be called
190 assert.doesNotThrow(() => {
191 // Import should work if extension is properly loaded
192 require('../extension');
193 }, 'Extension module should be loadable');
194 return; // Skip the rest if we can't find the extension by ID
195 }
196
197 assert.ok(extension, 'Extension should be loaded');
198
199 // Try to activate if not already active
200 if (!extension.isActive) {
201 try {
202 await extension.activate();
203 } catch (error) {
204 // Activation might fail in test environment, that's acceptable
205 }
206 }
207 });
208
213 test('should register all expected commands', async () => {
214 const commands = await vscode.commands.getCommands(true);
215
216 const expectedCommands = [
217 `${moduleName}.helloWorld`,
218 `${moduleName}.sayHelloWorld`,
219 `${moduleName}.injectHeader`,
220 `${moduleName}.refreshHeader`,
221 `${moduleName}.darling`,
222 `${moduleName}.author`,
223 `${moduleName}.displayRandomLogo`
224 ];
225
226 for (const expectedCommand of expectedCommands) {
227 assert.ok(
228 commands.includes(expectedCommand),
229 `Command '${expectedCommand}' should be registered`
230 );
231 }
232 });
233
238 test('should handle deactivation gracefully', () => {
239 // Test that deactivation doesn't throw errors
240 assert.doesNotThrow(() => {
241 deactivate();
242 }, 'Deactivation should not throw errors');
243 });
244 });
245
253 suite('Command Execution Tests', () => {
258 test('should execute helloWorld command without errors', async () => {
259 await assert.doesNotReject(async () => {
260 await vscode.commands.executeCommand(`${moduleName}.helloWorld`);
261 }, 'HelloWorld command should execute without errors');
262 });
263
268 test('should execute sayHelloWorld command with active editor', async () => {
269 // Create a test document
270 const testContent = '// Test file content\n';
271 const testFilePath = path.join(tempDir, 'test.ts');
272 await fs.writeFile(testFilePath, testContent);
273
274 const uri = vscode.Uri.file(testFilePath);
275 testDocument = await vscode.workspace.openTextDocument(uri);
276 testEditor = await vscode.window.showTextDocument(testDocument);
277
278 const originalLength = testDocument.getText().length;
279
280 await vscode.commands.executeCommand(`${moduleName}.sayHelloWorld`);
281
282 // Should have added content to the document
283 const newLength = testDocument.getText().length;
284 assert.ok(newLength > originalLength, 'Content should be added to the document');
285 });
286
291 test('should handle sayHelloWorld command with no active editor', async () => {
292 // Ensure no active editor
293 await vscode.commands.executeCommand('workbench.action.closeAllEditors');
294
295 // Command should not throw but may show an error message
296 await assert.doesNotReject(async () => {
297 await vscode.commands.executeCommand(`${moduleName}.sayHelloWorld`);
298 }, 'SayHelloWorld command should handle no active editor gracefully');
299 });
300
305 test('should execute injectHeader command', async () => {
306 await assert.doesNotReject(async () => {
307 await vscode.commands.executeCommand(`${moduleName}.injectHeader`);
308 }, 'InjectHeader command should execute without errors');
309 });
310
315 test('should execute refreshHeader command', async () => {
316 await assert.doesNotReject(async () => {
317 await vscode.commands.executeCommand(`${moduleName}.refreshHeader`);
318 }, 'RefreshHeader command should execute without errors');
319 });
320
325 test('should execute darling command', async () => {
326 await assert.doesNotReject(async () => {
327 await vscode.commands.executeCommand(`${moduleName}.darling`);
328 }, 'Darling command should execute without errors');
329 });
330
335 test('should execute author command', async () => {
336 await assert.doesNotReject(async () => {
337 await vscode.commands.executeCommand(`${moduleName}.author`);
338 }, 'Author command should execute without errors');
339 });
340
345 test('should execute displayRandomLogo command', async () => {
346 await assert.doesNotReject(async () => {
347 await vscode.commands.executeCommand(`${moduleName}.displayRandomLogo`);
348 }, 'DisplayRandomLogo command should execute without errors');
349 });
350 });
351
359 suite('File Information Extraction', () => {
364 test('should extract file info from TypeScript file', async () => {
365 const testContent = 'const test: string = "hello";';
366 const testFilePath = path.join(tempDir, 'test.ts');
367 await fs.writeFile(testFilePath, testContent);
368
369 const uri = vscode.Uri.file(testFilePath);
370 testDocument = await vscode.workspace.openTextDocument(uri);
371 testEditor = await vscode.window.showTextDocument(testDocument);
372
373 // Execute sayHelloWorld which uses getFileInfo internally
374 await vscode.commands.executeCommand(`${moduleName}.sayHelloWorld`);
375
376 const documentText = testDocument.getText();
377 assert.ok(documentText.includes('.ts'), 'Should detect TypeScript file extension');
378 assert.ok(documentText.includes('test.ts'), 'Should detect correct filename');
379 });
380
385 test('should extract file info from Python file', async () => {
386 const testContent = 'print("hello world")';
387 const testFilePath = path.join(tempDir, 'test.py');
388 await fs.writeFile(testFilePath, testContent);
389
390 const uri = vscode.Uri.file(testFilePath);
391 testDocument = await vscode.workspace.openTextDocument(uri);
392 testEditor = await vscode.window.showTextDocument(testDocument);
393
394 await vscode.commands.executeCommand(`${moduleName}.sayHelloWorld`);
395
396 const documentText = testDocument.getText();
397 assert.ok(documentText.includes('.py'), 'Should detect Python file extension');
398 assert.ok(documentText.includes('test.py'), 'Should detect correct filename');
399 });
400
405 test('should handle files without extensions', async () => {
406 const testContent = 'Some content without extension';
407 const testFilePath = path.join(tempDir, 'README');
408 await fs.writeFile(testFilePath, testContent);
409
410 const uri = vscode.Uri.file(testFilePath);
411 testDocument = await vscode.workspace.openTextDocument(uri);
412 testEditor = await vscode.window.showTextDocument(testDocument);
413
414 await vscode.commands.executeCommand(`${moduleName}.sayHelloWorld`);
415
416 const documentText = testDocument.getText();
417 assert.ok(documentText.includes('README'), 'Should detect filename without extension');
418 });
419 });
420
427 suite('Document Save Integration', () => {
432 test('should handle document save events', async () => {
433 const testContent = '// Initial content\n';
434 const testFilePath = path.join(tempDir, 'savetest.js');
435 await fs.writeFile(testFilePath, testContent);
436
437 const uri = vscode.Uri.file(testFilePath);
438 testDocument = await vscode.workspace.openTextDocument(uri);
439 testEditor = await vscode.window.showTextDocument(testDocument);
440
441 // Modify the document
442 await testEditor.edit(editBuilder => {
443 editBuilder.insert(new vscode.Position(1, 0), 'console.log("test");\n');
444 });
445
446 // Save should trigger the save event handler
447 await assert.doesNotReject(async () => {
448 if (testDocument) {
449 await testDocument.save();
450 }
451 }, 'Document save should be handled without errors');
452 });
453
458 test('should prevent concurrent save operations', async () => {
459 const testContent = '// Concurrent save test\n';
460 const testFilePath = path.join(tempDir, 'concurrent.js');
461 await fs.writeFile(testFilePath, testContent);
462
463 const uri = vscode.Uri.file(testFilePath);
464 testDocument = await vscode.workspace.openTextDocument(uri);
465 testEditor = await vscode.window.showTextDocument(testDocument);
466
467 // Modify document
468 await testEditor.edit(editBuilder => {
469 editBuilder.insert(new vscode.Position(1, 0), 'const test = true;\n');
470 });
471
472 // Multiple rapid saves should be handled safely
473 const savePromises = testDocument ? [
474 testDocument.save(),
475 testDocument.save(),
476 testDocument.save()
477 ] : [];
478
479 await assert.doesNotReject(async () => {
480 await Promise.all(savePromises);
481 }, 'Concurrent saves should be handled safely');
482 });
483 });
484
491 suite('Workspace Management', () => {
496 test('should handle workspace without folders', () => {
497 // Test workspace name refresh when no workspace folders exist
498 assert.doesNotThrow(() => {
499 // The refreshWorkspaceName function should handle empty workspace gracefully
500 vscode.commands.executeCommand(`${moduleName}.injectHeader`);
501 }, 'Should handle workspace without folders');
502 });
503
508 test('should detect workspace folders when available', () => {
509 // This test depends on the actual workspace setup
510 const workspaceFolders = vscode.workspace.workspaceFolders;
511 if (workspaceFolders && workspaceFolders.length > 0) {
512 assert.ok(workspaceFolders[0].name, 'Should have workspace folder name');
513 }
514 });
515 });
516
523 suite('Error Handling and Edge Cases', () => {
528 test('should handle commands with invalid context gracefully', async () => {
529 // Close all editors to test edge cases
530 await vscode.commands.executeCommand('workbench.action.closeAllEditors');
531
532 // Commands should handle the lack of active editor gracefully
533 await assert.doesNotReject(async () => {
534 await vscode.commands.executeCommand(`${moduleName}.refreshHeader`);
535 }, 'Should handle refresh header without active document');
536 });
537
542 test('should handle file operations on read-only files', async () => {
543 // Create a test file
544 const testContent = '// Read-only test\n';
545 const testFilePath = path.join(tempDir, 'readonly.js');
546 await fs.writeFile(testFilePath, testContent);
547
548 const uri = vscode.Uri.file(testFilePath);
549 testDocument = await vscode.workspace.openTextDocument(uri);
550 testEditor = await vscode.window.showTextDocument(testDocument);
551
552 // Try to execute sayHelloWorld - should handle gracefully even if file becomes read-only
553 await assert.doesNotReject(async () => {
554 await vscode.commands.executeCommand(`${moduleName}.sayHelloWorld`);
555 }, 'Should handle file operations gracefully');
556 });
557 });
558
565 suite('Performance and Resource Management', () => {
570 test('should complete activation within reasonable time', function () {
571 this.timeout(5000); // 5 second timeout
572
573 // Activation should be fast
574 const extension = vscode.extensions.getExtension('asperguide.asperheader');
575 if (extension && !extension.isActive) {
576 return extension.activate();
577 }
578 });
579
584 test('should handle multiple rapid command executions', async () => {
585 // Execute multiple commands rapidly
586 const commandPromises = [
587 vscode.commands.executeCommand(`${moduleName}.helloWorld`),
588 vscode.commands.executeCommand(`${moduleName}.displayRandomLogo`),
589 vscode.commands.executeCommand(`${moduleName}.author`),
590 vscode.commands.executeCommand(`${moduleName}.darling`)
591 ];
592
593 await assert.doesNotReject(async () => {
594 await Promise.all(commandPromises);
595 }, 'Should handle multiple rapid command executions');
596 });
597
602 test('should clean up resources properly', () => {
603 // Test that deactivation and cleanup work properly
604 assert.doesNotThrow(() => {
605 deactivate();
606 }, 'Resource cleanup should not throw errors');
607 });
608 });
609
616 suite('Module Integration Tests', () => {
621 test('should have properly initialized all modules', async () => {
622 // These commands depend on module initialization
623 const moduleCommands = [
624 `${moduleName}.darling`,
625 `${moduleName}.author`,
626 `${moduleName}.displayRandomLogo`,
627 `${moduleName}.injectHeader`
628 ];
629
630 for (const command of moduleCommands) {
631 await assert.doesNotReject(async () => {
632 await vscode.commands.executeCommand(command);
633 }, `Module integration test for ${command} should not fail`);
634 }
635 });
636
641 test('should handle asset loading failures gracefully', async () => {
642 // Commands should still execute even if some assets fail to load
643 await assert.doesNotReject(async () => {
644 await vscode.commands.executeCommand(`${moduleName}.displayRandomLogo`);
645 await vscode.commands.executeCommand(`${moduleName}.darling`);
646 await vscode.commands.executeCommand(`${moduleName}.author`);
647 }, 'Should handle asset loading issues gracefully');
648 });
649 });
650});
export const moduleName
Module identifier used in package.json and extension marketplace.
Definition constants.ts:59
import fs from fs
Definition esbuild.js:9
import *as vscode from vscode
import *as path from path
import *as assert from assert
function export deactivate()
Extension cleanup and deactivation handler.
Definition extension.ts:371
function async activate(context:vscode.ExtensionContext)
Main extension activation entry point.
Definition extension.ts:243
export const Record< string,(...args:any[])=> string