// Test edge cases and error handling for folder navigation const { test } = require('node:test'); const assert = require('assert'); const fs = require('fs'); const path = require('path'); const UIManager = require('./utils/renderer/UIManager'); test('Navigation handles non-existent folder gracefully', async () => { const mockUIManager = { currentDirectory: '/test/source', selectedDirEl: { textContent: '' }, appState: { navigationStack: ['/test/source'], currentDepth: 0, addToNavigationStack: function(dir) { this.navigationStack.push(dir); this.currentDepth = this.navigationStack.length - 1; }, getNavigationStack: function() { return this.navigationStack; } }, scanDirectory: async (dir) => { return { success: true, files: [] }; }, _logAuditEvent: async () => {}, openDirectory: async (directory) => { this.appState.addToNavigationStack(directory); this.currentDirectory = directory; }, handleFolderClick: async (folderPath, folderName) => { console.log('Folder clicked:', folderName, 'at path:', folderPath); // Validate folder path exists if (!fs.existsSync(folderPath) || !fs.statSync(folderPath).isDirectory()) { console.error('Invalid folder path:', folderPath); return; // Don't navigate } // Open the folder await this.openDirectory(folderPath); } }; // Mock fs.existsSync and fs.statSync const originalExistsSync = fs.existsSync; const originalStatSync = fs.statSync; fs.existsSync = (path) => { if (path === '/non/existent/path') return false; return originalExistsSync(path); }; fs.statSync = () => { throw new Error('ENOENT: no such file or directory'); }; const consoleError = console.error; console.error = () => {}; try { mockUIManager.handleFolderClick('/non/existent/path', 'nonexistent'); // Verify navigation stack wasn't modified assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 1); assert.strictEqual(mockUIManager.currentDirectory, '/test/source'); console.log('Non-existent folder test passed'); } finally { fs.existsSync = originalExistsSync; fs.statSync = originalStatSync; console.error = consoleError; } }); test('Navigation handles permission errors gracefully', async () => { const mockUIManager = { currentDirectory: '/test/source', selectedDirEl: { textContent: '' }, appState: { navigationStack: ['/test/source'], currentDepth: 0, addToNavigationStack: function(dir) { this.navigationStack.push(dir); this.currentDepth = this.navigationStack.length - 1; }, getNavigationStack: function() { return this.navigationStack; } }, scanDirectory: async (dir) => { return { success: true, files: [] }; }, _logAuditEvent: async () => {}, openDirectory: async (directory) => { this.appState.addToNavigationStack(directory); this.currentDirectory = directory; }, handleFolderClick: async (folderPath, folderName) => { console.log('Folder clicked:', folderName, 'at path:', folderPath); // Validate folder path exists if (!fs.existsSync(folderPath) || !fs.statSync(folderPath).isDirectory()) { console.error('Invalid folder path:', folderPath); return; // Don't navigate } // Open the folder await this.openDirectory(folderPath); } }; // Mock fs.statSync to throw permission error const originalStatSync = fs.statSync; fs.statSync = (path) => { if (path === '/permission/denied') { const error = new Error('EACCES: permission denied'); error.code = 'EACCES'; throw error; } return originalStatSync(path); }; const consoleError = console.error; console.error = () => {}; try { mockUIManager.handleFolderClick('/permission/denied', 'denied'); // Verify navigation stack wasn't modified assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 1); console.log('Permission error handling test passed'); } finally { fs.statSync = originalStatSync; console.error = consoleError; } }); test('Breadcrumb navigation handles empty stack', () => { const mockUIManager = { selectedDirEl: { textContent: '' }, appState: { getNavigationStack: () => [], canGoBack: () => false } }; mockUIManager.updateBreadcrumbNavigation = function() { const breadcrumbs = this.appState.getNavigationStack(); // Should not throw for empty stack breadcrumbs.forEach(() => { assert.ok(false, 'Should not iterate over empty stack'); }); return document.createElement('div'); }; try { const container = mockUIManager.updateBreadcrumbNavigation(); assert.ok(true, 'Should handle empty stack'); } catch (error) { assert.ok(false, 'Should not throw for empty stack'); } }); test('Breadcrumb navigation handles null stack', () => { const mockUIManager = { selectedDirEl: { textContent: '' }, appState: { getNavigationStack: () => null, canGoBack: () => false } }; mockUIManager.updateBreadcrumbNavigation = function() { // Should handle null stack gracefully return document.createElement('div'); }; try { const container = mockUIManager.updateBreadcrumbNavigation(); assert.ok(true, 'Should handle null stack'); } catch (error) { assert.ok(false, 'Should not throw for null stack'); } }); test('Go back from root does not cause errors', () => { const mockUIManager = { currentDirectory: '/test/source', selectedDirEl: { textContent: '' }, appState: { navigationStack: ['/test/source'], currentDepth: 0, goBack: function() { if (this.currentDepth > 0) { this.navigationStack.pop(); this.currentDepth--; return this.navigationStack[this.currentDepth]; } return null; }, canGoBack: function() { return this.currentDepth > 0; } }, scanDirectory: async (dir) => { return { success: true, files: [] }; }, _logAuditEvent: async () => {}, goBack: async () => { const parentDirectory = this.appState.goBack(); if (parentDirectory) { this.currentDirectory = parentDirectory; } return parentDirectory; }, updateBreadcrumbNavigation: function() { // Mock breadcrumb update } }; // Try to go back when already at root const result = mockUIManager.goBack(); assert.strictEqual(result, null, 'Should return null when at root'); assert.strictEqual(mockUIManager.currentDirectory, '/test/source', 'Should not change directory'); }); test('Special folder names are valid', () => { const specialNames = ['extras', 'behind the scenes', 'delete', 'trailers', 'featurettes']; specialNames.forEach(name => { // Verify name doesn't contain invalid characters assert.ok(!name.includes(path.sep), `Folder name "${name}" contains path separator`); // Verify name is not too long assert.ok(name.length <= 255, `Folder name "${name}" exceeds 255 characters`); // Verify name is not empty assert.ok(name.length > 0, `Folder name "${name}" should not be empty`); }); console.log('Special folder names validation passed'); }); test('Navigation preserves file list state', async () => { const testDir = path.join(__dirname, 'test_state_preservation'); if (!fs.existsSync(testDir)) { fs.mkdirSync(testDir); } try { const mockUIManager = { currentDirectory: null, selectedDirEl: { textContent: '' }, fileListEl: { querySelectorAll: () => [] }, appState: { navigationStack: [], currentDepth: 0, addToNavigationStack: function(dir) { this.navigationStack.push(dir); this.currentDepth = this.navigationStack.length - 1; } }, scanDirectory: async (dir) => { // Mock different file lists for different directories if (dir === path.join(testDir, 'folder1')) { return { success: true, files: [{ name: 'file1.mp4' }] }; } else if (dir === path.join(testDir, 'folder2')) { return { success: true, files: [{ name: 'file2.mkv' }] }; } return { success: true, files: [] }; }, _logAuditEvent: async () => {}, openDirectory: async (directory) => { this.appState.addToNavigationStack(directory); this.currentDirectory = directory; const result = await this.scanDirectory(directory); return result; } }; // Navigate to folder1 const result1 = await mockUIManager.openDirectory(path.join(testDir, 'folder1')); assert.strictEqual(result1.files.length, 1); assert.strictEqual(result1.files[0].name, 'file1.mp4'); // Navigate to folder2 const result2 = await mockUIManager.openDirectory(path.join(testDir, 'folder2')); assert.strictEqual(result2.files.length, 1); assert.strictEqual(result2.files[0].name, 'file2.mkv'); // Go back to folder1 mockUIManager.appState.goBack(); assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'folder1')); console.log('File list state preservation test passed'); } finally { if (fs.existsSync(testDir)) { fs.rmSync(testDir, { recursive: true }); } } }); test('Multiple rapid navigations', async () => { const testDir = path.join(__dirname, 'test_rapid_nav'); if (!fs.existsSync(testDir)) { fs.mkdirSync(testDir); } try { const mockUIManager = { currentDirectory: null, selectedDirEl: { textContent: '' }, appState: { navigationStack: [], currentDepth: 0, addToNavigationStack: function(dir) { this.navigationStack.push(dir); this.currentDepth = this.navigationStack.length - 1; }, goBack: function() { if (this.currentDepth > 0) { this.navigationStack.pop(); this.currentDepth--; return this.navigationStack[this.currentDepth]; } return null; } }, scanDirectory: async (dir) => { return { success: true, files: [] }; }, _logAuditEvent: async () => {}, openDirectory: async (directory) => { this.appState.addToNavigationStack(directory); this.currentDirectory = directory; } }; // Rapidly navigate between directories for (let i = 0; i < 10; i++) { await mockUIManager.openDirectory(path.join(testDir, `folder${i}`)); } // Verify navigation stack assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 11); // testDir + 10 folders assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'folder9')); console.log('Multiple rapid navigations test passed'); } finally { if (fs.existsSync(testDir)) { fs.rmSync(testDir, { recursive: true }); } } }); test('Navigation with special characters in folder names', async () => { const testDir = path.join(__dirname, 'test_special_chars'); if (!fs.existsSync(testDir)) { fs.mkdirSync(testDir); } try { // Create folders with special characters const specialNames = ['folder with spaces', 'folder-dashes', 'folder_underscores']; for (const name of specialNames) { const folderPath = path.join(testDir, name); if (!fs.existsSync(folderPath)) { fs.mkdirSync(folderPath); } } const mockUIManager = { currentDirectory: null, selectedDirEl: { textContent: '' }, appState: { navigationStack: [], currentDepth: 0, addToNavigationStack: function(dir) { this.navigationStack.push(dir); this.currentDepth = this.navigationStack.length - 1; } }, scanDirectory: async (dir) => { return { success: true, files: [] }; }, _logAuditEvent: async () => {}, openDirectory: async (directory) => { this.appState.addToNavigationStack(directory); this.currentDirectory = directory; } }; // Navigate to folders with special characters for (const name of specialNames) { const folderPath = path.join(testDir, name); await mockUIManager.openDirectory(folderPath); assert.strictEqual(mockUIManager.currentDirectory, folderPath); } console.log('Special characters in folder names test passed'); } finally { if (fs.existsSync(testDir)) { fs.rmSync(testDir, { recursive: true }); } } }); console.log('All edge case tests passed!');