Asper Header  1.0.14
The header injector extension
Loading...
Searching...
No Matches
randomLogo.test.ts
Go to the documentation of this file.
1
32import * as assert from 'assert';
33import * as vscode from 'vscode';
34import * as fs from 'fs/promises';
35import * as path from 'path';
36import * as os from 'os';
37import { RandomLogo, logo } from '../modules/randomLogo';
38
43class MockWebview {
44 private _html: string = '';
45 private messageHandlers: Array<(message: any) => void> = [];
46
51 get html(): string {
52 return this._html;
53 }
54
59 set html(value: string) {
60 this._html = value;
61 }
62
68 onDidReceiveMessage(handler: (message: any) => void) {
69 this.messageHandlers.push(handler);
70 return { dispose: () => { } };
71 }
72
77 postMessage(message: any) {
78 this.messageHandlers.forEach(handler => handler(message));
79 }
80}
81
93 public webview: MockWebview;
94
103 public viewType: string,
104 public title: string,
105 public showOptions: vscode.ViewColumn,
106 public options: vscode.WebviewPanelOptions & vscode.WebviewOptions
107 ) {
108 this.webview = new MockWebview();
109 }
110}
111
116suite('RandomLogo Test Suite', () => {
117 let tempDir: string;
118 let logoDir: string;
119 let subDir: string;
120 let originalCreateWebviewPanel: typeof vscode.window.createWebviewPanel;
121 let capturedHtml: string;
122 let lastCreatedWebview: MockWebview;
123 let lastCreatedPanel: MockWebviewPanel;
124
129 setup(async () => {
130 // Create temporary directory structure for test files
131 tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'randomlogo-test-'));
132 logoDir = path.join(tempDir, 'logos');
133 subDir = path.join(logoDir, 'subdir');
134
135 await fs.mkdir(logoDir, { recursive: true });
136 await fs.mkdir(subDir, { recursive: true });
137
138 // Create mock ASCII art logo files
139 const logoFiles = [
140 {
141 name: 'simple-logo.txt',
142 content: ' **** \n * * \n * * \n **** '
143 },
144 {
145 name: 'complex-logo.txt',
146 content: '╔══════════════════════════════════════╗\n║ LOGO TITLE ║\n║ ║\n║ ██████ ██████ ███ ███ ██████ ║\n║ ██ ██ ██ ████ ████ ██ ██ ║\n║ ██ ██ ██ ██ ████ ██ ██████ ║\n║ ██ ██ ██ ██ ██ ██ ██ ║\n║ ██████ ██████ ██ ██ ██ ║\n║ ║\n╚══════════════════════════════════════╝'
147 },
148 {
149 name: 'multiline-logo.txt',
150 content: 'Line 1\nLine 2\nLine 3\nLine 4\nLine 5'
151 }
152 ];
153
154 const subDirLogoFiles = [
155 {
156 name: 'nested-logo.txt',
157 content: ' /\\_/\\ \n ( o.o ) \n > ^ < '
158 }
159 ];
160
161 // Write main logo files
162 for (const file of logoFiles) {
163 await fs.writeFile(path.join(logoDir, file.name), file.content, 'utf8');
164 }
165
166 // Write subdirectory logo files
167 for (const file of subDirLogoFiles) {
168 await fs.writeFile(path.join(subDir, file.name), file.content, 'utf8');
169 }
170
171 // Create non-logo files to test filtering
172 await fs.writeFile(path.join(logoDir, 'readme.md'), '# This is not a logo', 'utf8');
173 await fs.writeFile(path.join(logoDir, 'config.json'), '{"test": true}', 'utf8');
174
175 // Mock VS Code webview panel creation
176 originalCreateWebviewPanel = vscode.window.createWebviewPanel;
177 vscode.window.createWebviewPanel = (
178 viewType: string,
179 title: string,
180 showOptions: vscode.ViewColumn | { readonly viewColumn: vscode.ViewColumn; readonly preserveFocus?: boolean | undefined; },
181 options?: vscode.WebviewPanelOptions & vscode.WebviewOptions
182 ) => {
183 const column = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions;
184 const opts = options || {};
185 lastCreatedPanel = new MockWebviewPanel(viewType, title, column, opts);
186 lastCreatedWebview = lastCreatedPanel.webview;
187 capturedHtml = '';
188
189 // Intercept HTML setting
190 Object.defineProperty(lastCreatedWebview, 'html', {
191 get: () => capturedHtml,
192 set: (value: string) => {
193 capturedHtml = value;
194 }
195 });
196
197 return lastCreatedPanel as any;
198 };
199 });
200
205 teardown(async () => {
206 // Restore original VS Code API
207 if (originalCreateWebviewPanel) {
208 vscode.window.createWebviewPanel = originalCreateWebviewPanel;
209 }
210
211 // Clean up temporary files
212 try {
213 await fs.rm(tempDir, { recursive: true, force: true });
214 } catch (error) {
215 // Ignore cleanup errors in tests
216 }
217 });
218
223 suite('Constructor and Initialization', () => {
228 test('should create instance with default parameters', () => {
229 const randomLogo = new RandomLogo();
230 assert.ok(randomLogo instanceof RandomLogo);
231 });
232
237 test('should create instance with root directory parameter', async () => {
238 const randomLogo = new RandomLogo(logoDir);
239 assert.ok(randomLogo instanceof RandomLogo);
240
241 // Give some time for async file discovery
242 await new Promise(resolve => setTimeout(resolve, 50));
243 });
244
249 test('should create instance with both root directory and working directory', async () => {
250 const randomLogo = new RandomLogo(logoDir, tempDir);
251 assert.ok(randomLogo instanceof RandomLogo);
252
253 // Give some time for async file discovery
254 await new Promise(resolve => setTimeout(resolve, 50));
255 });
256
261 test('should handle undefined parameters gracefully', () => {
262 const randomLogo = new RandomLogo(undefined, undefined);
263 assert.ok(randomLogo instanceof RandomLogo);
264 });
265 });
266
271 suite('Directory and Path Management', () => {
276 test('should update root directory successfully', async () => {
277 const randomLogo = new RandomLogo();
278 const result = await randomLogo.updateRootDir(logoDir);
279 assert.strictEqual(result, true);
280 });
281
286 test('should handle invalid root directory gracefully', async () => {
287 const randomLogo = new RandomLogo();
288 const invalidPath = path.join(tempDir, 'nonexistent');
289 const result = await randomLogo.updateRootDir(invalidPath);
290 assert.strictEqual(result, false);
291 });
292
297 test('should update current working directory successfully', () => {
298 const randomLogo = new RandomLogo();
299 const result = randomLogo.updateCurrentWorkingDirectory(tempDir);
300 assert.strictEqual(result, true);
301 });
302
307 test('should handle relative paths with working directory', () => {
308 const randomLogo = new RandomLogo();
309 randomLogo.updateCurrentWorkingDirectory(tempDir);
310 assert.strictEqual(randomLogo.updateCurrentWorkingDirectory('./test'), true);
311 });
312 });
313
318 suite('Logo File Discovery and Management', () => {
323 test('should discover logo files in root directory', async () => {
324 const randomLogo = new RandomLogo(logoDir);
325
326 // Allow time for file discovery
327 await new Promise(resolve => setTimeout(resolve, 100));
328
329 // Try to get a logo to verify files were discovered
330 try {
331 const logo = await randomLogo.getRandomLogoFromFolder();
332 assert.ok(logo.fileName.endsWith('.txt'));
333 assert.ok(Array.isArray(logo.logoContent));
334 } catch (error) {
335 // If no files discovered, this would throw
336 assert.fail('Should have discovered logo files');
337 }
338 });
339
344 test('should discover logo files recursively in subdirectories', async () => {
345 const randomLogo = new RandomLogo(logoDir);
346
347 // Allow time for recursive file discovery
348 await new Promise(resolve => setTimeout(resolve, 100));
349
350 // Test multiple logo selections to potentially get the nested one
351 const logoFileNames: string[] = [];
352 for (let i = 0; i < 10; i++) {
353 try {
354 const logo = await randomLogo.getRandomLogoFromFolder();
355 logoFileNames.push(logo.fileName);
356 } catch (error) {
357 // Continue trying
358 }
359 }
360
361 // Should have found files from both directories
362 assert.ok(logoFileNames.length > 0, 'Should have found logo files');
363 assert.ok(logoFileNames.some(name => name.endsWith('.txt')), 'Should have .txt files');
364 });
365
370 test('should filter non-txt files correctly', async () => {
371 const randomLogo = new RandomLogo(logoDir);
372
373 // Allow time for file discovery
374 await new Promise(resolve => setTimeout(resolve, 100));
375
376 // Test multiple selections to verify only .txt files are included
377 for (let i = 0; i < 5; i++) {
378 try {
379 const logo = await randomLogo.getRandomLogoFromFolder();
380 assert.ok(logo.fileName.endsWith('.txt'), `Logo file ${logo.fileName} should be a .txt file`);
381 } catch (error) {
382 // May not have files, continue
383 }
384 }
385 });
386
391 test('should handle empty directories gracefully', async () => {
392 const emptyDir = path.join(tempDir, 'empty');
393 await fs.mkdir(emptyDir, { recursive: true });
394
395 const randomLogo = new RandomLogo(emptyDir);
396
397 // Allow time for file discovery
398 await new Promise(resolve => setTimeout(resolve, 50));
399
400 try {
401 await randomLogo.getRandomLogoFromFolder();
402 assert.fail('Should throw error for empty directory');
403 } catch (error) {
404 assert.ok(error instanceof Error);
405 }
406 });
407 });
408
413 suite('Random Selection Algorithm', () => {
418 test('should select different logos on multiple calls', async () => {
419 const randomLogo = new RandomLogo(logoDir);
420
421 // Allow time for file discovery
422 await new Promise(resolve => setTimeout(resolve, 100));
423
424 const selectedLogos = new Set<string>();
425
426 // Get multiple logo selections
427 for (let i = 0; i < 10; i++) {
428 try {
429 const logo = await randomLogo.getRandomLogoFromFolder();
430 selectedLogos.add(logo.fileName);
431 } catch (error) {
432 // Continue if some selections fail
433 }
434 }
435
436 // With 4 logo files, we should see some variety in 10 selections
437 assert.ok(selectedLogos.size > 0, 'Should select at least one logo');
438 });
439
444 test('should always return valid logo from dataset', async () => {
445 const randomLogo = new RandomLogo(logoDir);
446
447 // Allow time for file discovery
448 await new Promise(resolve => setTimeout(resolve, 100));
449
450 for (let i = 0; i < 5; i++) {
451 try {
452 const logo = await randomLogo.getRandomLogoFromFolder();
453
454 assert.ok(logo.fileName, 'Should have a filename');
455 assert.ok(logo.fileName.endsWith('.txt'), 'Should be a .txt file');
456 assert.ok(Array.isArray(logo.logoContent), 'Logo content should be an array');
457 assert.ok(logo.logoContent.length > 0, 'Logo should have content lines');
458 } catch (error) {
459 // If discovery failed, that's acceptable for this test
460 }
461 }
462 });
463
468 test('should handle single logo file correctly', async () => {
469 // Create directory with single logo file
470 const singleLogoDir = path.join(tempDir, 'single');
471 await fs.mkdir(singleLogoDir, { recursive: true });
472 await fs.writeFile(path.join(singleLogoDir, 'only-logo.txt'), 'Single\nLogo\nContent', 'utf8');
473
474 const randomLogo = new RandomLogo(singleLogoDir);
475
476 // Allow time for file discovery
477 await new Promise(resolve => setTimeout(resolve, 50));
478
479 const logo = await randomLogo.getRandomLogoFromFolder();
480
481 assert.strictEqual(logo.fileName, 'only-logo.txt');
482 assert.deepStrictEqual(logo.logoContent, ['Single', 'Logo', 'Content']);
483 });
484
489 test('should properly split logo content by lines', async () => {
490 const randomLogo = new RandomLogo(logoDir);
491
492 // Allow time for file discovery
493 await new Promise(resolve => setTimeout(resolve, 100));
494
495 const logo = await randomLogo.getRandomLogoFromFolder();
496
497 assert.ok(Array.isArray(logo.logoContent), 'Logo content should be an array of lines');
498
499 // Verify that lines were properly split
500 const joinedContent = logo.logoContent.join('\n');
501 assert.ok(joinedContent.length > 0, 'Content should not be empty');
502 });
503 });
504
509 suite('Error Handling and Edge Cases', () => {
514 test('should throw error when no root directory is set', async () => {
515 const randomLogo = new RandomLogo();
516
517 try {
518 await randomLogo.getRandomLogoFromFolder();
519 assert.fail('Should throw error when no root directory is set');
520 } catch (error) {
521 assert.ok(error instanceof Error);
522 }
523 });
524
529 test('should handle corrupted or empty logo files gracefully', async () => {
530 // Create directory with empty file
531 const emptyLogoDir = path.join(tempDir, 'empty-files');
532 await fs.mkdir(emptyLogoDir, { recursive: true });
533 await fs.writeFile(path.join(emptyLogoDir, 'empty-logo.txt'), '', 'utf8');
534
535 const randomLogo = new RandomLogo(emptyLogoDir);
536
537 // Allow time for file discovery
538 await new Promise(resolve => setTimeout(resolve, 50));
539
540 const logo = await randomLogo.getRandomLogoFromFolder();
541
542 assert.strictEqual(logo.fileName, 'empty-logo.txt');
543 assert.ok(Array.isArray(logo.logoContent));
544 // Empty file should result in array with empty string
545 assert.strictEqual(logo.logoContent.length, 1);
546 assert.strictEqual(logo.logoContent[0], '');
547 });
548
553 test('should handle permission errors during file discovery', async () => {
554 const randomLogo = new RandomLogo();
555
556 // Try to update to a path that definitely doesn't exist
557 const result = await randomLogo.updateRootDir('/nonexistent/path/that/should/not/exist');
558 assert.strictEqual(result, false);
559 });
560
565 test('should handle various line ending formats', async () => {
566 // Create files with different line endings
567 const lineEndingDir = path.join(tempDir, 'line-endings');
568 await fs.mkdir(lineEndingDir, { recursive: true });
569
570 await fs.writeFile(path.join(lineEndingDir, 'unix-endings.txt'), 'Line1\nLine2\nLine3', 'utf8');
571 await fs.writeFile(path.join(lineEndingDir, 'windows-endings.txt'), 'Line1\r\nLine2\r\nLine3', 'utf8');
572
573 const randomLogo = new RandomLogo(lineEndingDir);
574
575 // Allow time for file discovery
576 await new Promise(resolve => setTimeout(resolve, 50));
577
578 for (let i = 0; i < 5; i++) {
579 const logo = await randomLogo.getRandomLogoFromFolder();
580
581 assert.ok(Array.isArray(logo.logoContent));
582 assert.strictEqual(logo.logoContent.length, 3);
583 assert.strictEqual(logo.logoContent[0], 'Line1');
584 assert.strictEqual(logo.logoContent[1], 'Line2');
585 assert.strictEqual(logo.logoContent[2], 'Line3');
586 }
587 });
588 });
589
594 suite('HTML Content Generation', () => {
599 test('should generate valid HTML content for webview', async () => {
600 const randomLogo = new RandomLogo(logoDir);
601
602 // Allow time for file discovery
603 await new Promise(resolve => setTimeout(resolve, 100));
604
605 await randomLogo.displayRandomLogoInWindow();
606
607 assert.ok(capturedHtml, 'Should generate HTML content');
608 assert.ok(capturedHtml.includes('<!DOCTYPE html>'), 'Should be valid HTML');
609 assert.ok(capturedHtml.includes('<html lang="en">'), 'Should have language attribute');
610 assert.ok(capturedHtml.includes('<body>'), 'Should have body tag');
611 });
612
617 test('should include logo content in HTML', async () => {
618 const randomLogo = new RandomLogo(logoDir);
619
620 // Allow time for file discovery
621 await new Promise(resolve => setTimeout(resolve, 100));
622
623 await randomLogo.displayRandomLogoInWindow();
624
625 assert.ok(capturedHtml.includes('<pre id="ascii">'), 'Should include ASCII art container');
626 // HTML should contain some ASCII art content
627 const preMatch = capturedHtml.match(/<pre id="ascii">(.*?)<\/pre>/s);
628 assert.ok(preMatch, 'Should have ASCII art content');
629 assert.ok(preMatch[1].length > 0, 'ASCII art content should not be empty');
630 });
631
636 test('should include interactive buttons in HTML', async () => {
637 const randomLogo = new RandomLogo(logoDir);
638
639 // Allow time for file discovery
640 await new Promise(resolve => setTimeout(resolve, 100));
641
642 await randomLogo.displayRandomLogoInWindow();
643
644 assert.ok(capturedHtml.includes('id="copyBtn"'), 'Should include copy button');
645 assert.ok(capturedHtml.includes('id="zoomInBtn"'), 'Should include zoom in button');
646 assert.ok(capturedHtml.includes('id="zoomOutBtn"'), 'Should include zoom out button');
647 });
648
653 test('should include CSS styling', async () => {
654 const randomLogo = new RandomLogo(logoDir);
655
656 // Allow time for file discovery
657 await new Promise(resolve => setTimeout(resolve, 100));
658
659 await randomLogo.displayRandomLogoInWindow();
660
661 assert.ok(capturedHtml.includes('<style>'), 'Should include CSS styling');
662 assert.ok(capturedHtml.includes('font-family:'), 'Should have font family styling');
663 assert.ok(capturedHtml.includes('pre {'), 'Should have pre tag styling for ASCII art');
664 });
665
670 test('should include logo filename in display', async () => {
671 const randomLogo = new RandomLogo(logoDir);
672
673 // Allow time for file discovery
674 await new Promise(resolve => setTimeout(resolve, 100));
675
676 await randomLogo.displayRandomLogoInWindow();
677
678 // Should include filename in the title or content
679 assert.ok(
680 capturedHtml.includes('.txt') ||
681 lastCreatedPanel.title.includes('.txt'),
682 'Should display logo filename'
683 );
684 });
685
690 test('should handle empty or undefined logo content gracefully', async () => {
691 // Create directory with file that might cause issues
692 const problematicDir = path.join(tempDir, 'problematic');
693 await fs.mkdir(problematicDir, { recursive: true });
694 await fs.writeFile(path.join(problematicDir, 'empty.txt'), '', 'utf8');
695
696 const randomLogo = new RandomLogo(problematicDir);
697
698 // Allow time for file discovery
699 await new Promise(resolve => setTimeout(resolve, 50));
700
701 // Should not throw and should generate valid HTML
702 await randomLogo.displayRandomLogoInWindow();
703
704 assert.ok(capturedHtml, 'Should generate HTML even with empty content');
705 assert.ok(capturedHtml.includes('<!DOCTYPE html>'), 'Should be valid HTML');
706 });
707 });
708
713 suite('JavaScript Functionality', () => {
718 test('should include copy button script', async () => {
719 const randomLogo = new RandomLogo(logoDir);
720
721 // Allow time for file discovery
722 await new Promise(resolve => setTimeout(resolve, 100));
723
724 await randomLogo.displayRandomLogoInWindow();
725
726 assert.ok(capturedHtml.includes("getElementById('copyBtn')"), 'Should include copy button script');
727 assert.ok(capturedHtml.includes('navigator.clipboard.writeText'), 'Should use clipboard API');
728 assert.ok(capturedHtml.includes("vscode.postMessage({ type: 'copied' })"), 'Should post message to VS Code');
729 });
730
735 test('should include zoom functionality script', async () => {
736 const randomLogo = new RandomLogo(logoDir);
737
738 // Allow time for file discovery
739 await new Promise(resolve => setTimeout(resolve, 100));
740
741 await randomLogo.displayRandomLogoInWindow();
742
743 assert.ok(capturedHtml.includes('updateFontSize'), 'Should include font size update function');
744 assert.ok(capturedHtml.includes("getElementById('zoomInBtn')"), 'Should include zoom in handler');
745 assert.ok(capturedHtml.includes("getElementById('zoomOutBtn')"), 'Should include zoom out handler');
746 assert.ok(capturedHtml.includes('currentSize'), 'Should track current font size');
747 });
748
753 test('should include VS Code API integration', async () => {
754 const randomLogo = new RandomLogo(logoDir);
755
756 // Allow time for file discovery
757 await new Promise(resolve => setTimeout(resolve, 100));
758
759 await randomLogo.displayRandomLogoInWindow();
760
761 assert.ok(capturedHtml.includes('acquireVsCodeApi()'), 'Should acquire VS Code API');
762 assert.ok(capturedHtml.includes('vscode.postMessage'), 'Should use VS Code message posting');
763 });
764
769 test('should implement font size constraints in zoom', async () => {
770 const randomLogo = new RandomLogo(logoDir);
771
772 // Allow time for file discovery
773 await new Promise(resolve => setTimeout(resolve, 100));
774
775 await randomLogo.displayRandomLogoInWindow();
776
777 // Should have minimum size constraint
778 assert.ok(capturedHtml.includes('>= 2'), 'Should have minimum font size constraint');
779 // Should have size initialization
780 assert.ok(capturedHtml.includes('currentSize = 20'), 'Should initialize font size');
781 });
782 });
783
788 suite('Integration and Message Handling', () => {
793 test('should setup message handler for copy events', async () => {
794 const randomLogo = new RandomLogo(logoDir);
795
796 // Allow time for file discovery
797 await new Promise(resolve => setTimeout(resolve, 100));
798
799 let messageReceived = false;
800 let receivedMessage: any = null;
801
802 // Mock the message handler
803 const originalOnMessage = lastCreatedWebview?.onDidReceiveMessage;
804 if (lastCreatedWebview) {
805 lastCreatedWebview.onDidReceiveMessage = (handler: (message: any) => void) => {
806 return {
807 dispose: () => {
808 // Mock dispose
809 }
810 };
811 };
812 }
813
814 await randomLogo.displayRandomLogoInWindow();
815
816 // Verify webview was created
817 assert.ok(lastCreatedPanel, 'Should create webview panel');
818 assert.ok(lastCreatedWebview, 'Should create webview');
819 });
820
825 test('should create webview with correct parameters', async () => {
826 const randomLogo = new RandomLogo(logoDir);
827
828 // Allow time for file discovery
829 await new Promise(resolve => setTimeout(resolve, 100));
830
831 await randomLogo.displayRandomLogoInWindow();
832
833 assert.ok(lastCreatedPanel, 'Should create webview panel');
834 assert.strictEqual(lastCreatedPanel.showOptions, vscode.ViewColumn.One, 'Should use ViewColumn.One');
835 assert.ok(lastCreatedPanel.options.enableScripts, 'Should enable scripts');
836 assert.ok(lastCreatedPanel.title.endsWith('.txt'), 'Title should be the filename');
837 });
838
843 test('should handle message communication properly', async () => {
844 const randomLogo = new RandomLogo(logoDir);
845
846 // Allow time for file discovery
847 await new Promise(resolve => setTimeout(resolve, 100));
848
849 await randomLogo.displayRandomLogoInWindow();
850
851 // Simulate copy message
852 if (lastCreatedWebview && lastCreatedWebview.postMessage) {
853 lastCreatedWebview.postMessage({ type: 'copied' });
854 }
855
856 // Should not throw errors
857 assert.ok(true, 'Message handling should not throw errors');
858 });
859 });
860
865 suite('Performance and Memory Management', () => {
870 test('should handle multiple rapid logo selections', async () => {
871 const randomLogo = new RandomLogo(logoDir);
872
873 // Allow time for file discovery
874 await new Promise(resolve => setTimeout(resolve, 100));
875
876 const startTime = Date.now();
877
878 // Perform multiple rapid selections
879 for (let i = 0; i < 10; i++) {
880 try {
881 await randomLogo.getRandomLogoFromFolder();
882 } catch (error) {
883 // Some may fail, continue
884 }
885 }
886
887 const endTime = Date.now();
888 const duration = endTime - startTime;
889
890 // Should complete reasonably quickly (less than 5 seconds)
891 assert.ok(duration < 5000, 'Multiple selections should be reasonably fast');
892 });
893
898 test('should reuse file loader instances efficiently', async () => {
899 const randomLogo = new RandomLogo(logoDir);
900
901 // Allow time for file discovery
902 await new Promise(resolve => setTimeout(resolve, 100));
903
904 // Get multiple logos - should reuse the discovered loaders
905 const logo1 = await randomLogo.getRandomLogoFromFolder();
906 const logo2 = await randomLogo.getRandomLogoFromFolder();
907
908 assert.ok(logo1.fileName.endsWith('.txt'));
909 assert.ok(logo2.fileName.endsWith('.txt'));
910
911 // Both should be valid logo objects
912 assert.ok(Array.isArray(logo1.logoContent));
913 assert.ok(Array.isArray(logo2.logoContent));
914 });
915
920 test('should handle large numbers of logo files', async () => {
921 // Create directory with many logo files
922 const manyLogosDir = path.join(tempDir, 'many-logos');
923 await fs.mkdir(manyLogosDir, { recursive: true });
924
925 // Create 50 logo files
926 for (let i = 0; i < 50; i++) {
927 const content = `Logo ${i}\n${'*'.repeat(i % 10 + 1)}\nEnd`;
928 await fs.writeFile(path.join(manyLogosDir, `logo-${i}.txt`), content, 'utf8');
929 }
930
931 const randomLogo = new RandomLogo(manyLogosDir);
932
933 // Allow time for discovery of many files
934 await new Promise(resolve => setTimeout(resolve, 200));
935
936 // Should still work efficiently
937 const logo = await randomLogo.getRandomLogoFromFolder();
938 assert.ok(logo.fileName.startsWith('logo-'));
939 assert.ok(logo.fileName.endsWith('.txt'));
940 });
941
946 test('should handle directory updates without memory leaks', async () => {
947 const randomLogo = new RandomLogo(logoDir);
948
949 // Allow time for initial discovery
950 await new Promise(resolve => setTimeout(resolve, 100));
951
952 // Update to different directory multiple times
953 await randomLogo.updateRootDir(logoDir);
954 await randomLogo.updateRootDir(subDir);
955 await randomLogo.updateRootDir(logoDir);
956
957 // Should still work after multiple updates
958 const logo = await randomLogo.getRandomLogoFromFolder();
959 assert.ok(logo.fileName.endsWith('.txt'));
960 assert.ok(Array.isArray(logo.logoContent));
961 });
962 });
963
968 suite('File System Integration', () => {
973 test('should handle symbolic links appropriately', async () => {
974 // This test may not work on all systems, so we'll skip if symlink creation fails
975 try {
976 const symlinkDir = path.join(tempDir, 'symlink-test');
977 await fs.mkdir(symlinkDir, { recursive: true });
978
979 // Try to create a symlink to one of our logo files
980 const targetFile = path.join(logoDir, 'simple-logo.txt');
981 const symlinkFile = path.join(symlinkDir, 'symlinked-logo.txt');
982
983 await fs.symlink(targetFile, symlinkFile);
984
985 const randomLogo = new RandomLogo(symlinkDir);
986
987 // Allow time for file discovery
988 await new Promise(resolve => setTimeout(resolve, 50));
989
990 const logo = await randomLogo.getRandomLogoFromFolder();
991 assert.ok(logo.fileName.endsWith('.txt'));
992
993 } catch (error) {
994 // Skip test if symlinks aren't supported
995 console.log('Skipping symlink test - not supported on this system');
996 }
997 });
998
1003 test('should respect file system permissions', async () => {
1004 const randomLogo = new RandomLogo();
1005
1006 // Try to access a system directory that likely doesn't have logo files
1007 const result = await randomLogo.updateRootDir('/etc');
1008
1009 if (result) {
1010 // If it succeeded, try to get a logo (should fail or return no results)
1011 try {
1012 await randomLogo.getRandomLogoFromFolder();
1013 } catch (error) {
1014 // Expected if no .txt files in /etc
1015 assert.ok(error instanceof Error);
1016 }
1017 } else {
1018 // If it failed, that's also acceptable (permission denied)
1019 assert.ok(true, 'Permission handling works correctly');
1020 }
1021 });
1022 });
1023});
Mock implementation of VS Code webview panel for testing.
constructor(public viewType:string, public title:string, public showOptions:vscode.ViewColumn, public options:vscode.WebviewPanelOptions &vscode.WebviewOptions)
Constructs a new mock webview panel with specified configuration.
Mock implementation of VS Code webview for testing.
postMessage(message:any)
Posts a message to all registered handlers.
onDidReceiveMessage(handler:(message:any)=> void)
Registers a message handler for webview communication.
get html()
Gets the current HTML content of the webview.
export const randomLogo
Whether to use random logo selection instead of default logo.
Definition constants.ts:235
import fs from fs
Definition esbuild.js:9
Structure representing a loaded ASCII art logo with metadata.
import type
export const Record< string,(...args:any[])=> string
import *as vscode from vscode
import *as path from path
import *as os from os
import *as assert from assert
import *as vscode from vscode