feat: add comprehensive modal error regression tests and fix saveConversationHistory import

- Add 8 new modal-specific integration tests covering all session errors:
  * Modal import function validation
  * Correct import path verification
  * Generator function wrapper correctness
  * Window global function exports
  * Safe chat history handling
  * Error handling patterns
  * Export/import matching
  * Circular dependency prevention
- Fix saveConversationHistory import from ./chat.js to ./storage.js
- Window functions openTTSModal/openTranslateModal already properly exported in ui.js
This commit is contained in:
Russell Ballestrini 2025-07-03 18:27:11 -04:00
parent 1d40a055b0
commit bfdb742d9e
2 changed files with 152 additions and 1 deletions

View file

@ -582,6 +582,157 @@ test('DOM manipulation is safe', () => {
assert(testElement.style.position === 'relative', 'Style setting failed');
});
// ===== SECTION 9: MODAL-SPECIFIC ERROR REGRESSION TESTS =====
console.log('\n🎭 Section 9: Modal Error Regression Tests');
test('Modal import functions are properly defined', () => {
const modalContent = readFile('uncloseai-embed-modal.js');
// Check that all required import wrapper functions exist
const requiredFunctions = [
'async function sendMessageWithCustomHistory',
'async function fetchModelsFromEndpoints',
'async function loadConversationHistory',
'async function clearConversationHistory',
'async function saveConversationHistory',
'async function getChatHistory',
'async function updateChatHistory',
'async function getSelectedModel'
];
requiredFunctions.forEach(func => {
assert(modalContent.includes(func), `Modal missing function: ${func}`);
});
});
test('Modal imports target correct modules', () => {
const modalContent = readFile('uncloseai-embed-modal.js');
// Test correct import paths for frequently broken functions
const correctImports = [
'await import("./models.js").*fetchModelsFromEndpoints',
'await import("./storage.js").*loadConversationHistory',
'await import("./storage.js").*clearConversationHistory',
'await import("./storage.js").*saveConversationHistory',
'await import("./models.js").*getSelectedModel',
'await import("./chat.js").*sendMessageWithCustomHistory'
];
correctImports.forEach(importPattern => {
const regex = new RegExp(importPattern, 'g');
assert(regex.test(modalContent), `Modal missing correct import: ${importPattern}`);
});
});
test('Modal generator function wrapper is correct', () => {
const modalContent = readFile('uncloseai-embed-modal.js');
// Check that sendMessageWithCustomHistory is properly wrapped as generator
assert(
modalContent.includes('async function* sendMessageWithCustomHistory'),
'sendMessageWithCustomHistory should be async generator function'
);
assert(
modalContent.includes('yield* sendMessageWithCustomHistory(history)'),
'sendMessageWithCustomHistory should use yield* to delegate to imported generator'
);
});
test('Window global functions are exported from ui.js', () => {
const uiContent = readFile('ui.js');
// Check that window globals are properly exported to fix modal button errors
const requiredGlobals = [
'window.openTTSModal',
'window.openTranslateModal',
'window.toggleUncloseaiEmbeddedModal'
];
requiredGlobals.forEach(global => {
assert(uiContent.includes(global), `ui.js missing global export: ${global}`);
});
});
test('Modal chat history handling is safe', () => {
const modalContent = readFile('uncloseai-embed-modal.js');
// Verify that direct chatHistory access is replaced with function calls
const lines = modalContent.split('\n');
let directChatHistoryAccess = false;
lines.forEach((line, index) => {
// Look for direct chatHistory usage (not in comments)
if (line.includes('chatHistory') &&
!line.trim().startsWith('//') &&
!line.includes('getChatHistory') &&
!line.includes('updateChatHistory') &&
!line.includes('const') &&
!line.includes('function')) {
// Allow some specific safe patterns
if (!line.includes('chatHistory.find') || !line.includes('await getChatHistory()')) {
directChatHistoryAccess = true;
console.log(` Direct chatHistory access found at line ${index + 1}: ${line.trim()}`);
}
}
});
assert(!directChatHistoryAccess, 'Modal should not directly access chatHistory variable');
});
test('Modal error handling patterns are implemented', () => {
const modalContent = readFile('uncloseai-embed-modal.js');
// Check for proper error handling in async functions
const asyncFunctionPattern = /async function[^{]*{[^}]*}/g;
const matches = modalContent.match(asyncFunctionPattern);
if (matches) {
// At least some async functions should have try-catch or error handling
const hasTryCatch = modalContent.includes('try {') || modalContent.includes('catch');
const hasErrorLog = modalContent.includes('console.error') || modalContent.includes('console.warn');
assert(
hasTryCatch || hasErrorLog,
'Modal should have error handling in async functions'
);
}
});
test('Modal function exports match their imports', () => {
const files = ['chat.js', 'storage.js', 'models.js', 'tts.js'];
const modalContent = readFile('uncloseai-embed-modal.js');
files.forEach(file => {
const fileContent = readFile(file);
const exports = extractExports(fileContent);
// Check that functions imported by modal are actually exported
exports.forEach(exportedFunc => {
if (modalContent.includes(`${exportedFunc}`)) {
const importPattern = new RegExp(`await import\\("\\.\/${file}"\\)[^}]*${exportedFunc}`, 'g');
if (importPattern.test(modalContent)) {
assert(true, `${file} exports ${exportedFunc} which modal imports`);
}
}
});
});
});
test('Modal prevents circular dependency with chat.js', () => {
const modalContent = readFile('uncloseai-embed-modal.js');
const chatContent = readFile('chat.js');
// Modal should only import from chat.js, not vice versa
const chatImportsModal = chatContent.includes('./uncloseai-embed-modal.js');
assert(!chatImportsModal, 'chat.js should not import from modal to prevent circular dependency');
// Modal should use dynamic imports from chat.js
const modalImportsChat = modalContent.includes('await import("./chat.js")');
assert(modalImportsChat, 'Modal should use dynamic imports from chat.js');
});
// Cleanup
dom.window.close();

View file

@ -56,7 +56,7 @@ async function loadConversationHistory() {
}
async function saveConversationHistory(history) {
const { saveConversationHistory } = await import("./chat.js");
const { saveConversationHistory } = await import("./storage.js");
return saveConversationHistory(history);
}