MovieMapper/test-folder-navigation-integration.js
Jarian Cottingham 4764bedafc Add file editing via double-click and fix TVDB ID appending logic
- Add double-click handler to make file names editable in UI
- Remove duplicate IPC handler for rename-file in renderer.js
- Fix checkEpisodeCountMatch to properly validate sequential episodes
- Fix begin-mapping to handle both season folder and show folder cases
- Improve open-file-in-player handler to properly log results
2026-02-26 19:20:15 -06:00

444 lines
14 KiB
JavaScript

// Integration tests 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');
// Create temporary test directory structure
function setupTestDirectory() {
const testDir = path.join(__dirname, 'test_integration_dir');
// Create main directory
if (!fs.existsSync(testDir)) {
fs.mkdirSync(testDir);
}
// Create subdirectories
const subdirs = ['folder1', 'folder2', 'extras', 'behind the scenes'];
for (const subdir of subdirs) {
const subdirPath = path.join(testDir, subdir);
if (!fs.existsSync(subdirPath)) {
fs.mkdirSync(subdirPath);
}
}
// Create nested structure
const nestedDir = path.join(testDir, 'nested');
if (!fs.existsSync(nestedDir)) {
fs.mkdirSync(nestedDir);
}
const deepDir = path.join(nestedDir, 'deep');
if (!fs.existsSync(deepDir)) {
fs.mkdirSync(deepDir);
}
// Create some mock files
fs.writeFileSync(path.join(testDir, 'file1.mp4'), '');
fs.writeFileSync(path.join(testDir, 'file2.mkv'), '');
return testDir;
}
function cleanupTestDirectory(testDir) {
if (fs.existsSync(testDir)) {
fs.rmSync(testDir, { recursive: true });
}
}
test('Full folder navigation workflow', async () => {
const testDir = setupTestDirectory();
try {
// Verify test directory structure
assert.ok(fs.existsSync(testDir), 'Test directory should exist');
const subdirs = ['folder1', 'folder2', 'extras', 'behind the scenes'];
for (const subdir of subdirs) {
assert.ok(fs.existsSync(path.join(testDir, subdir)), `Subdirectory ${subdir} should exist`);
}
// Create a mock UIManager
const mockUIManager = {
currentDirectory: null,
selectedDirEl: { textContent: '' },
appState: {
navigationStack: [],
currentDepth: 0,
addToNavigationStack: function(dir) {
this.navigationStack.push(dir);
this.currentDepth = this.navigationStack.length - 1;
},
getNavigationStack: function() {
return this.navigationStack;
},
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 () => {},
openDirectory: async (directory) => {
mockUIManager.appState.addToNavigationStack(directory);
mockUIManager.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 mockUIManager.openDirectory(folderPath);
},
updateBreadcrumbNavigation: function() {
// Mock breadcrumb update
}
};
// Navigate to folder1
await mockUIManager.openDirectory(path.join(testDir, 'folder1'));
assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'folder1'));
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 2); // testDir + folder1
// Navigate to folder2
await mockUIManager.openDirectory(path.join(testDir, 'folder2'));
assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'folder2'));
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 3);
// Navigate to extras
await mockUIManager.openDirectory(path.join(testDir, 'extras'));
assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'extras'));
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 4);
// Go back to folder2
const backDir1 = mockUIManager.appState.goBack();
assert.strictEqual(backDir1, path.join(testDir, 'folder2'));
assert.strictEqual(mockUIManager.appState.canGoBack(), true);
// Go back to folder1
const backDir2 = mockUIManager.appState.goBack();
assert.strictEqual(backDir2, path.join(testDir, 'folder1'));
assert.strictEqual(mockUIManager.appState.canGoBack(), true);
// Go back to testDir
const backDir3 = mockUIManager.appState.goBack();
assert.strictEqual(backDir3, testDir);
assert.strictEqual(mockUIManager.appState.canGoBack(), false);
console.log('Full navigation workflow test passed');
} finally {
cleanupTestDirectory(testDir);
}
});
test('Navigation stack preserves history correctly', async () => {
const testDir = setupTestDirectory();
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;
},
canGoBack: function() {
return this.currentDepth > 0;
}
},
openDirectory: async (directory) => {
mockUIManager.appState.addToNavigationStack(directory);
mockUIManager.currentDirectory = directory;
}
};
// Navigate: testDir -> nested -> deep
await mockUIManager.openDirectory(path.join(testDir, 'nested'));
await mockUIManager.openDirectory(path.join(testDir, 'nested', 'deep'));
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 3);
assert.strictEqual(mockUIManager.appState.currentDepth, 2);
// Go back to nested
mockUIManager.appState.goBack();
assert.strictEqual(mockUIManager.appState.currentDepth, 1);
// Navigate to extras (from nested)
await mockUIManager.openDirectory(path.join(testDir, 'extras'));
// Verify forward history is cleared
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 3);
assert.strictEqual(mockUIManager.appState.currentDepth, 2);
assert.strictEqual(mockUIManager.appState.getNavigationStack()[2], path.join(testDir, 'extras'));
console.log('Navigation stack history preservation test passed');
} finally {
cleanupTestDirectory(testDir);
}
});
test('Audit logging for navigation events', async () => {
const testDir = setupTestDirectory();
try {
let auditLog = [];
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 (action, details) => {
auditLog.push({ action, details });
},
openDirectory: async (directory) => {
mockUIManager.appState.addToNavigationStack(directory);
mockUIManager.currentDirectory = directory;
await this._logAuditEvent('navigate_to_directory', {
directory: directory,
navigationType: 'forward'
});
}
};
// Navigate to a directory
await mockUIManager.openDirectory(path.join(testDir, 'folder1'));
// Verify audit log
assert.strictEqual(auditLog.length, 1);
assert.strictEqual(auditLog[0].action, 'navigate_to_directory');
assert.strictEqual(auditLog[0].details.directory, path.join(testDir, 'folder1'));
assert.strictEqual(auditLog[0].details.navigationType, 'forward');
// Navigate back
mockUIManager.appState.goBack();
await mockUIManager._logAuditEvent('navigate_back', {
fromDirectory: path.join(testDir, 'folder1'),
toDirectory: testDir,
navigationType: 'back'
});
assert.strictEqual(auditLog.length, 2);
assert.strictEqual(auditLog[1].action, 'navigate_back');
assert.strictEqual(auditLog[1].details.navigationType, 'back');
console.log('Audit logging test passed');
} finally {
cleanupTestDirectory(testDir);
}
});
test('Special folder navigation', async () => {
const testDir = setupTestDirectory();
try {
const mockUIManager = {
currentDirectory: null,
selectedDirEl: { textContent: '' },
appState: {
navigationStack: [],
currentDepth: 0,
addToNavigationStack: function(dir) {
this.navigationStack.push(dir);
this.currentDepth = this.navigationStack.length - 1;
},
canGoBack: function() {
return this.currentDepth > 0;
}
},
openDirectory: async (directory) => {
mockUIManager.appState.addToNavigationStack(directory);
mockUIManager.currentDirectory = directory;
}
};
// Navigate to special folders
await mockUIManager.openDirectory(path.join(testDir, 'extras'));
assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'extras'));
await mockUIManager.openDirectory(path.join(testDir, 'behind the scenes'));
assert.strictEqual(mockUIManager.currentDirectory, path.join(testDir, 'behind the scenes'));
// Verify navigation stack
const stack = mockUIManager.appState.getNavigationStack();
assert.ok(stack.includes(path.join(testDir, 'extras')));
assert.ok(stack.includes(path.join(testDir, 'behind the scenes')));
console.log('Special folder navigation test passed');
} finally {
cleanupTestDirectory(testDir);
}
});
test('Navigation with nested directories', async () => {
const testDir = setupTestDirectory();
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;
},
canGoBack: function() {
return this.currentDepth > 0;
}
},
openDirectory: async (directory) => {
mockUIManager.appState.addToNavigationStack(directory);
mockUIManager.currentDirectory = directory;
}
};
// Navigate deeply: testDir -> nested -> deep
await mockUIManager.openDirectory(path.join(testDir, 'nested'));
await mockUIManager.openDirectory(path.join(testDir, 'nested', 'deep'));
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 3);
assert.strictEqual(mockUIManager.appState.currentDepth, 2);
// Go back multiple times
const back1 = mockUIManager.appState.goBack();
assert.strictEqual(back1, path.join(testDir, 'nested'));
assert.strictEqual(mockUIManager.appState.currentDepth, 1);
const back2 = mockUIManager.appState.goBack();
assert.strictEqual(back2, testDir);
assert.strictEqual(mockUIManager.appState.currentDepth, 0);
assert.strictEqual(mockUIManager.appState.canGoBack(), false);
console.log('Nested directory navigation test passed');
} finally {
cleanupTestDirectory(testDir);
}
});
test('Error handling for non-existent folders', async () => {
const testDir = setupTestDirectory();
try {
const mockUIManager = {
currentDirectory: testDir,
selectedDirEl: { textContent: '' },
appState: {
navigationStack: [testDir],
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) => {
mockUIManager.appState.addToNavigationStack(directory);
mockUIManager.currentDirectory = directory;
}
};
// Try to navigate to non-existent folder
const nonExistentPath = path.join(testDir, 'non-existent');
const consoleError = console.error;
console.error = () => {}; // Suppress error output
try {
mockUIManager.handleFolderClick(nonExistentPath, 'non-existent');
// Should handle gracefully
assert.ok(true, 'Should handle non-existent folder');
} finally {
console.error = consoleError;
}
// Verify navigation stack wasn't modified
assert.strictEqual(mockUIManager.appState.getNavigationStack().length, 1);
console.log('Error handling test passed');
} finally {
cleanupTestDirectory(testDir);
}
});
console.log('All integration tests passed!');