Asper Header  1.0.14
The header injector extension
Loading...
Searching...
No Matches
watermark.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 { Watermark, watermark } from '../modules/watermark';
38
43interface MockWatermarkData {
44 Logo: string[];
45 fontName: string;
46}
47
55class MockWebview {
56 private _html: string = '';
57 private messageHandlers: Array<(message: any) => void> = [];
58
63 get html(): string {
64 return this._html;
65 }
66
71 set html(value: string) {
72 this._html = value;
73 }
74
80 onDidReceiveMessage(handler: (message: any) => void) {
81 this.messageHandlers.push(handler);
82 return { dispose: () => { } };
83 }
84
89 postMessage(message: any) {
90 this.messageHandlers.forEach(handler => handler(message));
91 }
92}
93
104class MockWebviewPanel {
105 public webview: MockWebview;
106
115 public viewType: string,
116 public title: string,
117 public showOptions: vscode.ViewColumn,
118 public options: vscode.WebviewPanelOptions & vscode.WebviewOptions
119 ) {
120 this.webview = new MockWebview();
121 }
122}
123
128suite('Watermark Test Suite', () => {
129 let tempDir: string;
130 let testFilePath: string;
131 let mockWatermarks: MockWatermarkData[];
132 let originalCreateWebviewPanel: typeof vscode.window.createWebviewPanel;
133 let capturedHtml: string;
134 let lastCreatedWebview: MockWebview;
135 let lastCreatedPanel: MockWebviewPanel;
136
141 setup(async () => {
142 // Create temporary directory for test files
143 tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'watermark-test-'));
144 testFilePath = path.join(tempDir, 'test-watermarks.json');
145
146 // Create comprehensive mock watermark data
147 mockWatermarks = [
148 {
149 Logo: [
150 " _ _____ _ ____ ___ _ _ _____ _____ _____ _ _ _ _____ ____",
151 "/ \\ /|/ __// \\ /|/ __\\\\ \\// / \\ / __//__ __Y __// \\ / \\ / \\/ __// __\\",
152 "| |_||| \\ | |\\ ||| \\/| \\ / | | | \\ / \\ | \\ | | | | | || \\ | \\/|",
153 "| | ||| /_ | | \\||| / / / | |_/\\| /_ | | | /_ | |_/\\| |_/\\| || /_ | /",
154 "\\_/ \\|\\____\\\\_/ \\|\\_/\\_\\/_/ \\____/\\____\\ \\_/ \\____\\\\____/\\____/\\_/\\____\\\\_/\\_\\",
155 ""
156 ],
157 fontName: "Avatar"
158 },
159 {
160 Logo: [
161 " _ _ _ _ _ _ _",
162 " | | | | | | | | | | (_)",
163 " | |__| | ___ _ __ _ __ _ _ | | ___| |_ ___| | |_ ___ _ __",
164 " | __ |/ _ \\ '_ \\| '__| | | | | | / _ \\ __/ _ \\ | | |/ _ \\ '__|",
165 " | | | | __/ | | | | | |_| | | |___| __/ || __/ | | | __/ |",
166 " |_| |_|\\___|_| |_|_| \\__, | |______\\___|\\__\\___|_|_|_|\\___|_|",
167 " __/ |",
168 " |___/"
169 ],
170 fontName: "Big"
171 },
172 {
173 Logo: [
174 " _ _ ____ _ _ ____ _ _ __ ____ ____ ____ __ __ ____ ____ ____",
175 "( )_( )( ___)( \\( )( _ \\( \\/ ) ( ) ( ___)(_ _)( ___)( ) ( ) (_ _)( ___)( _ \\",
176 " ) _ ( )__) ) ( ) / \\ / )(__ )__) )( )__) )(__ )(__ _)(_ )__) ) /",
177 "(_) (_)(____)(_)\\_)(_)\\_) (__) (____)(____) (__) (____)(____)(____)(____)(____)(_)\\_)"
178 ],
179 fontName: "Bulbhead"
180 },
181 {
182 Logo: [
183 "╦ ╦┌─┐┌┐┌┬─┐┬ ┬ ╦ ┌─┐┌┬┐┌─┐┬ ┬ ┬┌─┐┬─┐",
184 "╠═╣├┤ │││├┬┘└┬┘ ║ ├┤ │ ├┤ │ │ │├┤ ├┬┘",
185 "╩ ╩└─┘┘└┘┴└─ ┴ ╩═╝└─┘ ┴ └─┘┴─┘┴─┘┴└─┘┴└─"
186 ],
187 fontName: "Calvin S"
188 },
189 {
190 Logo: [
191 "██ ██ ███████ ███ ██ ██████ ██ ██ ██ ███████ ████████ ███████ ██ ██ ██ ███████ ██████",
192 "██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██",
193 "███████ █████ ██ ██ ██ ██████ ████ ██ █████ ██ █████ ██ ██ ██ █████ ██████",
194 "██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██",
195 "██ ██ ███████ ██ ████ ██ ██ ██ ███████ ███████ ██ ███████ ███████ ███████ ██ ███████ ██ ██"
196 ],
197 fontName: "Chunky"
198 }
199 ];
200
201 // Setup webview mocking
202 capturedHtml = '';
203 originalCreateWebviewPanel = vscode.window.createWebviewPanel;
204 vscode.window.createWebviewPanel = (
205 viewType: string,
206 title: string,
207 showOptions: vscode.ViewColumn | { readonly viewColumn: vscode.ViewColumn; readonly preserveFocus?: boolean | undefined; },
208 options?: vscode.WebviewPanelOptions & vscode.WebviewOptions
209 ) => {
210 const column = typeof showOptions === 'object' ? showOptions.viewColumn : showOptions;
211 const opts = options || {};
212 lastCreatedPanel = new MockWebviewPanel(viewType, title, column, opts);
213 lastCreatedWebview = lastCreatedPanel.webview;
214
215 // Capture HTML when it's set
216 Object.defineProperty(lastCreatedWebview, 'html', {
217 get: () => capturedHtml,
218 set: (value: string) => {
219 capturedHtml = value;
220 }
221 });
222
223 return lastCreatedPanel as any;
224 };
225 });
226
231 teardown(async () => {
232 // Cleanup temporary directory
233 try {
234 await fs.rm(tempDir, { recursive: true, force: true });
235 } catch (error) {
236 // Ignore cleanup errors
237 }
238
239 // Restore original VS Code API
240 if (originalCreateWebviewPanel) {
241 vscode.window.createWebviewPanel = originalCreateWebviewPanel;
242 }
243
244 // Reset captured data
245 capturedHtml = '';
246 });
247
252 suite('Constructor and Initialization', () => {
257 test('should create instance with default parameters', () => {
258 const watermark = new Watermark();
259 assert.ok(watermark instanceof Watermark, 'Should create Watermark instance');
260 });
261
266 test('should create instance with file path parameter', async () => {
267 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
268 const watermark = new Watermark(testFilePath);
269 assert.ok(watermark instanceof Watermark, 'Should create Watermark instance with file path');
270 });
271
276 test('should create instance with both file path and working directory', async () => {
277 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
278 const watermark = new Watermark(testFilePath, tempDir);
279 assert.ok(watermark instanceof Watermark, 'Should create Watermark instance with both parameters');
280 });
281
286 test('should handle undefined parameters gracefully', () => {
287 const watermark = new Watermark(undefined, undefined);
288 assert.ok(watermark instanceof Watermark, 'Should handle undefined parameters');
289 });
290 });
291
296 suite('File Path Management', () => {
301 test('should update file path successfully', async () => {
302 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
303 const watermark = new Watermark();
304
305 const success = await watermark.updateFilePath(testFilePath);
306 assert.strictEqual(success, true, 'Should return true for successful file path update');
307 });
308
313 test('should update working directory successfully', async () => {
314 const watermark = new Watermark();
315
316 const success = await watermark.updateCurrentWorkingDirectory(tempDir);
317 assert.strictEqual(success, true, 'Should return true for successful working directory update');
318 });
319
324 test('should handle relative paths with working directory', async () => {
325 const relativePath = 'watermarks.json';
326 const fullPath = path.join(tempDir, relativePath);
327 await fs.writeFile(fullPath, JSON.stringify(mockWatermarks));
328
329 const watermark = new Watermark();
330 await watermark.updateCurrentWorkingDirectory(tempDir);
331 const success = await watermark.updateFilePath(relativePath);
332
333 assert.strictEqual(success, true, 'Should handle relative paths correctly');
334 });
335 });
336
341 suite('Watermark Data Loading and Validation', () => {
346 test('should load and parse watermark data correctly', async () => {
347 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
348 const watermark = new Watermark(testFilePath, tempDir);
349
350 const result = await watermark.getRandomWatermark();
351
352 // Validate watermark structure
353 assert.ok(typeof result.fontName === 'string', 'Watermark should have string fontName');
354 assert.ok(Array.isArray(result.watermark), 'Watermark should have array watermark content');
355 assert.ok(result.watermark.length > 0, 'Watermark array should not be empty');
356 assert.ok(result.watermark.every(line => typeof line === 'string'), 'All watermark lines should be strings');
357 });
358
363 test('should correctly map Logo to watermark property', async () => {
364 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
365 const watermark = new Watermark(testFilePath, tempDir);
366
367 const result = await watermark.getRandomWatermark();
368
369 assert.ok(Array.isArray(result.watermark), 'watermark property should be an array');
370 assert.ok(result.watermark.length > 0, 'watermark content should not be empty');
371 assert.ok(result.watermark.every(line => typeof line === 'string'), 'All watermark content lines should be strings');
372 });
373
378 test('should handle various font name formats', async () => {
379 const diverseFonts = [
380 { Logo: ["Simple", "Font"], fontName: "SimpleName" },
381 { Logo: ["Complex", "Font"], fontName: "Complex Font With Spaces" },
382 { Logo: ["Special", "Font"], fontName: "Special-Characters_123" },
383 { Logo: ["Unicode", "Font"], fontName: "Unicode Font ♦ ♠ ♣ ♥" }
384 ];
385 await fs.writeFile(testFilePath, JSON.stringify(diverseFonts));
386 const watermark = new Watermark(testFilePath, tempDir);
387
388 for (let i = 0; i < 10; i++) {
389 const result = await watermark.getRandomWatermark();
390 assert.ok(typeof result.fontName === 'string', 'Font name should be a string');
391 assert.ok(result.fontName.length > 0, 'Font name should not be empty');
392 }
393 });
394
399 test('should handle empty logo arrays gracefully', async () => {
400 const emptyLogoWatermarks = [
401 { Logo: [], fontName: "EmptyLogo" },
402 { Logo: ["Non-empty"], fontName: "NonEmpty" }
403 ];
404 await fs.writeFile(testFilePath, JSON.stringify(emptyLogoWatermarks));
405 const watermark = new Watermark(testFilePath, tempDir);
406
407 const result = await watermark.getRandomWatermark();
408
409 assert.ok(Array.isArray(result.watermark), 'watermark should be an array');
410 assert.ok(typeof result.fontName === 'string', 'fontName should be a string');
411 });
412 });
413
418 suite('Random Selection Algorithm', () => {
423 test('should select different watermarks on multiple calls', async () => {
424 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
425 const watermark = new Watermark(testFilePath, tempDir);
426
427 const selectedFonts = new Set<string>();
428
429 // Get multiple watermark selections
430 for (let i = 0; i < 20; i++) {
431 const result = await watermark.getRandomWatermark();
432 selectedFonts.add(result.fontName);
433 }
434
435 // With 5 watermark fonts, we should see some variety in 20 selections
436 assert.ok(selectedFonts.size >= 2, 'Should select multiple different fonts');
437 });
438
443 test('should always return valid watermark from dataset', async () => {
444 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
445 const watermark = new Watermark(testFilePath, tempDir);
446
447 const expectedFonts = new Set(mockWatermarks.map(w => w.fontName));
448
449 for (let i = 0; i < 10; i++) {
450 const result = await watermark.getRandomWatermark();
451
452 assert.ok(result.fontName, 'Should have a font name');
453 assert.ok(expectedFonts.has(result.fontName), `Font name ${result.fontName} should be from the dataset`);
454 assert.ok(Array.isArray(result.watermark), 'Watermark content should be an array');
455 assert.ok(result.watermark.length >= 0, 'Watermark should have valid content');
456 }
457 });
458
463 test('should handle single watermark file correctly', async () => {
464 const singleWatermark = [mockWatermarks[0]];
465 await fs.writeFile(testFilePath, JSON.stringify(singleWatermark));
466 const watermark = new Watermark(testFilePath, tempDir);
467
468 for (let i = 0; i < 5; i++) {
469 const result = await watermark.getRandomWatermark();
470
471 assert.strictEqual(result.fontName, mockWatermarks[0].fontName);
472 assert.deepStrictEqual(result.watermark, mockWatermarks[0].Logo);
473 }
474 });
475
480 test('should properly handle watermark content arrays', async () => {
481 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
482 const watermark = new Watermark(testFilePath, tempDir);
483
484 const result = await watermark.getRandomWatermark();
485
486 assert.ok(Array.isArray(result.watermark), 'Watermark content should be an array');
487
488 // Verify that each line is a string
489 result.watermark.forEach((line, index) => {
490 assert.ok(typeof line === 'string', `Watermark line ${index} should be a string`);
491 });
492 });
493 });
494
499 suite('Error Handling and Edge Cases', () => {
504 test('should throw error for empty JSON file', async () => {
505 await fs.writeFile(testFilePath, JSON.stringify([]));
506 const watermark = new Watermark(testFilePath, tempDir);
507
508 try {
509 await watermark.getRandomWatermark();
510 assert.fail('Should throw error for empty watermark array');
511 } catch (error) {
512 assert.ok(error instanceof Error, 'Should throw an Error instance');
513 }
514 });
515
520 test('should throw error for invalid JSON format', async () => {
521 await fs.writeFile(testFilePath, '{ invalid json');
522 const watermark = new Watermark(testFilePath, tempDir);
523
524 try {
525 await watermark.getRandomWatermark();
526 assert.fail('Should throw error for invalid JSON');
527 } catch (error) {
528 assert.ok(error instanceof Error, 'Should throw an Error instance');
529 }
530 });
531
536 test('should throw error for non-array JSON content', async () => {
537 await fs.writeFile(testFilePath, JSON.stringify({ not: "an array" }));
538 const watermark = new Watermark(testFilePath, tempDir);
539
540 try {
541 await watermark.getRandomWatermark();
542 assert.fail('Should throw error for non-array JSON content');
543 } catch (error) {
544 assert.ok(error instanceof Error, 'Should throw an Error instance');
545 }
546 });
547
552 test('should handle missing Logo property gracefully', async () => {
553 const malformedWatermarks = [
554 { fontName: "MissingLogo" }, // Missing Logo property
555 { Logo: ["Valid", "Content"], fontName: "Valid" }
556 ];
557 await fs.writeFile(testFilePath, JSON.stringify(malformedWatermarks));
558 const watermark = new Watermark(testFilePath, tempDir);
559
560 // Should handle gracefully or throw meaningful error
561 try {
562 const result = await watermark.getRandomWatermark();
563 // If it succeeds, it should have valid structure
564 assert.ok(typeof result.fontName === 'string');
565 } catch (error) {
566 // If it fails, that's acceptable for malformed data
567 assert.ok(error instanceof Error);
568 }
569 });
570
575 test('should handle missing fontName property gracefully', async () => {
576 const malformedWatermarks = [
577 { Logo: ["Missing", "Font", "Name"] }, // Missing fontName property
578 { Logo: ["Valid", "Content"], fontName: "Valid" }
579 ];
580 await fs.writeFile(testFilePath, JSON.stringify(malformedWatermarks));
581 const watermark = new Watermark(testFilePath, tempDir);
582
583 // Should handle gracefully or throw meaningful error
584 try {
585 const result = await watermark.getRandomWatermark();
586 // If it succeeds, it should have valid structure
587 assert.ok(Array.isArray(result.watermark));
588 } catch (error) {
589 // If it fails, that's acceptable for malformed data
590 assert.ok(error instanceof Error);
591 }
592 });
593
598 test('should handle file not found error', async () => {
599 const watermark = new Watermark();
600
601 // Try to update to non-existent file
602 const nonExistentPath = path.join(tempDir, 'non-existent.json');
603 await watermark.updateFilePath(nonExistentPath);
604
605 try {
606 await watermark.getRandomWatermark();
607 // May succeed if no file path is set and returns default
608 } catch (error) {
609 // Expected for missing file
610 assert.ok(error instanceof Error);
611 }
612 });
613 });
614
619 suite('HTML Content Generation', () => {
624 test('should generate valid HTML content for webview', async () => {
625 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
626 const watermark = new Watermark(testFilePath, tempDir);
627
628 await watermark.displayRandomAuthorWatermarkInWindow();
629
630 assert.ok(capturedHtml, 'Should generate HTML content');
631 assert.ok(capturedHtml.includes('<!DOCTYPE html>'), 'Should be valid HTML');
632 assert.ok(capturedHtml.includes('<html lang="en">'), 'Should have language attribute');
633 assert.ok(capturedHtml.includes('<body>'), 'Should have body tag');
634 });
635
640 test('should include watermark content in HTML', async () => {
641 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
642 const watermark = new Watermark(testFilePath, tempDir);
643
644 await watermark.displayRandomAuthorWatermarkInWindow();
645
646 assert.ok(capturedHtml.includes('<pre id="ascii">'), 'Should include ASCII art container');
647 // HTML should contain some watermark content
648 const preMatch = capturedHtml.match(/<pre id="ascii">(.*?)<\/pre>/s);
649 assert.ok(preMatch, 'Should have ASCII art content');
650 assert.ok(preMatch[1].length > 0, 'ASCII art content should not be empty');
651 });
652
657 test('should include interactive buttons in HTML', async () => {
658 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
659 const watermark = new Watermark(testFilePath, tempDir);
660
661 await watermark.displayRandomAuthorWatermarkInWindow();
662
663 assert.ok(capturedHtml.includes('id="copyBtn"'), 'Should include copy button');
664 assert.ok(capturedHtml.includes('id="zoomInBtn"'), 'Should include zoom in button');
665 assert.ok(capturedHtml.includes('id="zoomOutBtn"'), 'Should include zoom out button');
666 });
667
672 test('should include CSS styling', async () => {
673 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
674 const watermark = new Watermark(testFilePath, tempDir);
675
676 await watermark.displayRandomAuthorWatermarkInWindow();
677
678 assert.ok(capturedHtml.includes('<style>'), 'Should include CSS styling');
679 assert.ok(capturedHtml.includes('font-family:'), 'Should have font family styling');
680 assert.ok(capturedHtml.includes('pre {'), 'Should have pre tag styling for ASCII art');
681 });
682
687 test('should include font name in display', async () => {
688 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
689 const watermark = new Watermark(testFilePath, tempDir);
690
691 await watermark.displayRandomAuthorWatermarkInWindow();
692
693 // Should include font name in the content or title
694 const includesFontName = mockWatermarks.some(w =>
695 capturedHtml.includes(w.fontName) || lastCreatedPanel.title.includes(w.fontName)
696 );
697 assert.ok(includesFontName, 'Should display font name');
698 });
699
704 test('should include author information', async () => {
705 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
706 const watermark = new Watermark(testFilePath, tempDir);
707
708 await watermark.displayRandomAuthorWatermarkInWindow();
709
710 assert.ok(capturedHtml.includes('Henry Letellier'), 'Should include author name');
711 });
712
717 test('should handle empty watermark content gracefully', async () => {
718 const emptyWatermarks = [{ Logo: [], fontName: "Empty" }];
719 await fs.writeFile(testFilePath, JSON.stringify(emptyWatermarks));
720 const watermark = new Watermark(testFilePath, tempDir);
721
722 // Should not throw and should generate valid HTML
723 await watermark.displayRandomAuthorWatermarkInWindow();
724
725 assert.ok(capturedHtml, 'Should generate HTML even with empty content');
726 assert.ok(capturedHtml.includes('<!DOCTYPE html>'), 'Should be valid HTML');
727 });
728 });
729
734 suite('JavaScript Functionality', () => {
739 test('should include copy button script', async () => {
740 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
741 const watermark = new Watermark(testFilePath, tempDir);
742
743 await watermark.displayRandomAuthorWatermarkInWindow();
744
745 assert.ok(capturedHtml.includes("getElementById('copyBtn')"), 'Should include copy button script');
746 assert.ok(capturedHtml.includes('navigator.clipboard.writeText'), 'Should use clipboard API');
747 assert.ok(capturedHtml.includes("vscode.postMessage({ type: 'copied' })"), 'Should post message to VS Code');
748 });
749
754 test('should include zoom functionality script', async () => {
755 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
756 const watermark = new Watermark(testFilePath, tempDir);
757
758 await watermark.displayRandomAuthorWatermarkInWindow();
759
760 assert.ok(capturedHtml.includes('updateFontSize'), 'Should include font size update function');
761 assert.ok(capturedHtml.includes("getElementById('zoomInBtn')"), 'Should include zoom in handler');
762 assert.ok(capturedHtml.includes("getElementById('zoomOutBtn')"), 'Should include zoom out handler');
763 assert.ok(capturedHtml.includes('currentSize'), 'Should track current font size');
764 });
765
770 test('should include VS Code API integration', async () => {
771 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
772 const watermark = new Watermark(testFilePath, tempDir);
773
774 await watermark.displayRandomAuthorWatermarkInWindow();
775
776 assert.ok(capturedHtml.includes('acquireVsCodeApi()'), 'Should acquire VS Code API');
777 assert.ok(capturedHtml.includes('vscode.postMessage'), 'Should use VS Code message posting');
778 });
779
784 test('should implement font size constraints in zoom', async () => {
785 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
786 const watermark = new Watermark(testFilePath, tempDir);
787
788 await watermark.displayRandomAuthorWatermarkInWindow();
789
790 // Should have minimum size constraint
791 assert.ok(capturedHtml.includes('>= 2'), 'Should have minimum font size constraint');
792 // Should have size initialization
793 assert.ok(capturedHtml.includes('currentSize = 20'), 'Should initialize font size');
794 });
795
800 test('should include debug logging for zoom functionality', async () => {
801 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
802 const watermark = new Watermark(testFilePath, tempDir);
803
804 await watermark.displayRandomAuthorWatermarkInWindow();
805
806 assert.ok(capturedHtml.includes('console.log'), 'Should include console logging');
807 assert.ok(capturedHtml.includes('sizeDifference'), 'Should log size difference');
808 assert.ok(capturedHtml.includes('currentSize'), 'Should log current size');
809 });
810 });
811
816 suite('Integration and Message Handling', () => {
821 test('should setup message handler for copy events', async () => {
822 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
823 const watermark = new Watermark(testFilePath, tempDir);
824
825 await watermark.displayRandomAuthorWatermarkInWindow();
826
827 // Verify webview was created with message handling
828 assert.ok(lastCreatedPanel, 'Should create webview panel');
829 assert.ok(lastCreatedWebview, 'Should create webview');
830
831 // Simulate copy message
832 if (lastCreatedWebview && lastCreatedWebview.postMessage) {
833 lastCreatedWebview.postMessage({ type: 'copied' });
834 }
835
836 // Should not throw errors
837 assert.ok(true, 'Message handling should not throw errors');
838 });
839
844 test('should create webview with correct parameters', async () => {
845 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
846 const watermark = new Watermark(testFilePath, tempDir);
847
848 await watermark.displayRandomAuthorWatermarkInWindow();
849
850 assert.ok(lastCreatedPanel, 'Should create webview panel');
851 assert.strictEqual(lastCreatedPanel.showOptions, vscode.ViewColumn.One, 'Should use ViewColumn.One');
852 assert.ok(lastCreatedPanel.options.enableScripts, 'Should enable scripts');
853
854 // Title should be one of the font names
855 const expectedFontNames = mockWatermarks.map(w => w.fontName);
856 assert.ok(expectedFontNames.includes(lastCreatedPanel.title), 'Title should be a font name');
857 });
858
863 test('should handle message communication properly', async () => {
864 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
865 const watermark = new Watermark(testFilePath, tempDir);
866
867 await watermark.displayRandomAuthorWatermarkInWindow();
868
869 // Test different message types
870 const testMessages = [
871 { type: 'copied' },
872 { type: 'unknown' },
873 { type: 'test', data: 'value' }
874 ];
875
876 testMessages.forEach(message => {
877 if (lastCreatedWebview && lastCreatedWebview.postMessage) {
878 try {
879 lastCreatedWebview.postMessage(message);
880 } catch (error) {
881 assert.fail(`Should handle message ${JSON.stringify(message)} without error`);
882 }
883 }
884 });
885
886 assert.ok(true, 'All message types handled without errors');
887 });
888 });
889
894 suite('Performance and Memory Management', () => {
899 test('should handle multiple rapid watermark selections', async () => {
900 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
901 const watermark = new Watermark(testFilePath, tempDir);
902
903 const startTime = Date.now();
904
905 // Perform multiple rapid selections
906 for (let i = 0; i < 50; i++) {
907 await watermark.getRandomWatermark();
908 }
909
910 const endTime = Date.now();
911 const duration = endTime - startTime;
912
913 // Should complete reasonably quickly (less than 5 seconds)
914 assert.ok(duration < 5000, 'Multiple selections should be reasonably fast');
915 });
916
921 test('should reuse file loader instances efficiently', async () => {
922 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
923 const watermark = new Watermark(testFilePath, tempDir);
924
925 // Get multiple watermarks - should reuse the loaded data
926 const watermark1 = await watermark.getRandomWatermark();
927 const watermark2 = await watermark.getRandomWatermark();
928
929 assert.ok(typeof watermark1.fontName === 'string');
930 assert.ok(typeof watermark2.fontName === 'string');
931
932 // Both should be valid watermark objects
933 assert.ok(Array.isArray(watermark1.watermark));
934 assert.ok(Array.isArray(watermark2.watermark));
935 });
936
941 test('should handle large watermark collections', async () => {
942 // Create large collection of watermarks
943 const largeCollection: MockWatermarkData[] = [];
944 for (let i = 0; i < 100; i++) {
945 largeCollection.push({
946 Logo: [`Watermark ${i}`, `Line 2 of ${i}`, `Line 3 of ${i}`],
947 fontName: `Font${i}`
948 });
949 }
950
951 await fs.writeFile(testFilePath, JSON.stringify(largeCollection));
952 const watermark = new Watermark(testFilePath, tempDir);
953
954 // Should still work efficiently
955 const result = await watermark.getRandomWatermark();
956 assert.ok(result.fontName.startsWith('Font'));
957 assert.ok(Array.isArray(result.watermark));
958 assert.strictEqual(result.watermark.length, 3);
959 });
960
965 test('should handle file path updates without memory leaks', async () => {
966 const watermark = new Watermark();
967
968 // Create multiple temporary files
969 const file1 = path.join(tempDir, 'watermarks1.json');
970 const file2 = path.join(tempDir, 'watermarks2.json');
971
972 await fs.writeFile(file1, JSON.stringify([mockWatermarks[0]]));
973 await fs.writeFile(file2, JSON.stringify([mockWatermarks[1]]));
974
975 // Update file path multiple times
976 await watermark.updateFilePath(file1);
977 const result1 = await watermark.getRandomWatermark();
978
979 await watermark.updateFilePath(file2);
980 const result2 = await watermark.getRandomWatermark();
981
982 await watermark.updateFilePath(file1);
983 const result3 = await watermark.getRandomWatermark();
984
985 // Should get appropriate results from each file
986 assert.strictEqual(result1.fontName, mockWatermarks[0].fontName);
987 assert.strictEqual(result2.fontName, mockWatermarks[1].fontName);
988 assert.strictEqual(result3.fontName, mockWatermarks[0].fontName);
989 });
990 });
991
996 suite('Edge Cases and Robustness', () => {
1001 test('should handle Unicode characters in watermarks', async () => {
1002 const unicodeWatermarks = [
1003 {
1004 Logo: [
1005 "╔══════════════════════════════════════╗",
1006 "║ UNICODE LOGO ║",
1007 "║ ♦ ♠ ♣ ♥ ★ ☆ ☀ ☁ ☂ ☃ ☄ ☎ ☏ ☐ ☑ ║",
1008 "║ ♪ ♫ ♬ ♭ ♮ ♯ ♰ ♱ ♲ ♳ ♴ ♵ ♶ ♷ ♸ ║",
1009 "╚══════════════════════════════════════╝"
1010 ],
1011 fontName: "Unicode Test"
1012 }
1013 ];
1014
1015 await fs.writeFile(testFilePath, JSON.stringify(unicodeWatermarks));
1016 const watermark = new Watermark(testFilePath, tempDir);
1017
1018 const result = await watermark.getRandomWatermark();
1019
1020 assert.strictEqual(result.fontName, "Unicode Test");
1021 assert.ok(result.watermark.some(line => line.includes('♦')), 'Should preserve Unicode characters');
1022 });
1023
1028 test('should handle very long watermark lines', async () => {
1029 const longLineWatermark = [
1030 {
1031 Logo: [
1032 "A".repeat(1000), // Very long line
1033 "Short line",
1034 "B".repeat(500) // Medium long line
1035 ],
1036 fontName: "Long Lines"
1037 }
1038 ];
1039
1040 await fs.writeFile(testFilePath, JSON.stringify(longLineWatermark));
1041 const watermark = new Watermark(testFilePath, tempDir);
1042
1043 const result = await watermark.getRandomWatermark();
1044
1045 assert.strictEqual(result.fontName, "Long Lines");
1046 assert.strictEqual(result.watermark[0].length, 1000);
1047 assert.strictEqual(result.watermark[2].length, 500);
1048 });
1049
1054 test('should handle watermarks with many lines', async () => {
1055 const manyLinesWatermark = [
1056 {
1057 Logo: Array.from({ length: 100 }, (_, i) => `Line ${i + 1}`),
1058 fontName: "Many Lines"
1059 }
1060 ];
1061
1062 await fs.writeFile(testFilePath, JSON.stringify(manyLinesWatermark));
1063 const watermark = new Watermark(testFilePath, tempDir);
1064
1065 const result = await watermark.getRandomWatermark();
1066
1067 assert.strictEqual(result.fontName, "Many Lines");
1068 assert.strictEqual(result.watermark.length, 100);
1069 assert.strictEqual(result.watermark[0], "Line 1");
1070 assert.strictEqual(result.watermark[99], "Line 100");
1071 });
1072
1077 test('should handle special characters in font names', async () => {
1078 const specialNameWatermarks = [
1079 { Logo: ["Test"], fontName: "Font/With\\Special:Characters" },
1080 { Logo: ["Test"], fontName: "Font<>With|Symbols" },
1081 { Logo: ["Test"], fontName: "Font\"With'Quotes" }
1082 ];
1083
1084 await fs.writeFile(testFilePath, JSON.stringify(specialNameWatermarks));
1085 const watermark = new Watermark(testFilePath, tempDir);
1086
1087 for (let i = 0; i < 10; i++) {
1088 const result = await watermark.getRandomWatermark();
1089 assert.ok(typeof result.fontName === 'string');
1090 assert.ok(result.fontName.length > 0);
1091 }
1092 });
1093
1098 test('should handle JSON with extra properties', async () => {
1099 const extendedWatermarks = [
1100 {
1101 Logo: ["Test", "Watermark"],
1102 fontName: "Test Font",
1103 extraProperty: "Should be ignored",
1104 author: "Unknown",
1105 version: 1.0
1106 }
1107 ];
1108
1109 await fs.writeFile(testFilePath, JSON.stringify(extendedWatermarks));
1110 const watermark = new Watermark(testFilePath, tempDir);
1111
1112 const result = await watermark.getRandomWatermark();
1113
1114 // Should only use Logo and fontName properties
1115 assert.strictEqual(result.fontName, "Test Font");
1116 assert.deepStrictEqual(result.watermark, ["Test", "Watermark"]);
1117 });
1118 });
1119
1124 suite('Type Safety and Data Integrity', () => {
1129 test('should maintain correct types throughout selection process', async () => {
1130 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
1131 const watermark = new Watermark(testFilePath, tempDir);
1132
1133 for (let i = 0; i < 10; i++) {
1134 const result = await watermark.getRandomWatermark();
1135
1136 // Type assertions
1137 assert.strictEqual(typeof result, 'object', 'Result should be an object');
1138 assert.strictEqual(typeof result.fontName, 'string', 'fontName should be string');
1139 assert.ok(Array.isArray(result.watermark), 'watermark should be array');
1140
1141 // Content validation
1142 result.watermark.forEach((line, index) => {
1143 assert.strictEqual(typeof line, 'string', `Line ${index} should be string`);
1144 });
1145 }
1146 });
1147
1152 test('should validate watermark interface compliance', async () => {
1153 await fs.writeFile(testFilePath, JSON.stringify(mockWatermarks));
1154 const watermark = new Watermark(testFilePath, tempDir);
1155
1156 const result = await watermark.getRandomWatermark();
1157
1158 // Check that result satisfies watermark interface
1159 const watermarkInterface: watermark = result;
1160 assert.ok(watermarkInterface.fontName, 'Should have fontName property');
1161 assert.ok(watermarkInterface.watermark, 'Should have watermark property');
1162
1163 // Ensure no extra properties (should only have these two)
1164 const keys = Object.keys(result);
1165 assert.ok(keys.includes('fontName'), 'Should include fontName');
1166 assert.ok(keys.includes('watermark'), 'Should include watermark');
1167 });
1168 });
1169});
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.
import fs from fs
Definition esbuild.js:9
Interface for test watermark data matching the JSON structure.
Structure representing a loaded ASCII art watermark with font 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