/** * Collections Auto-Index Toggle UI Test * * Tests the auto-index toggle on the collections page that controls * whether documents are automatically indexed when uploaded or downloaded. * * What this tests: * - Toggle loads initial state from settings API * - Toggle changes persist to settings API * - Toggle reflects correct state after page reload * - Error handling when API calls fail * * Prerequisites: Web server running on http://127.0.0.1:5000 * * Usage: node tests/ui_tests/test_collections_auto_index.js */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions, takeScreenshot } = require('./puppeteer_config'); const BASE_URL = 'http://127.0.0.1:5000'; const COLLECTIONS_URL = `${BASE_URL}/library/collections`; const SETTINGS_API_KEY = 'research_library.auto_index_enabled'; async function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function runTests() { const browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); const authHelper = new AuthHelper(page, BASE_URL); let passed = 0; let failed = 0; // Monitor console errors const consoleErrors = []; page.on('console', msg => { if (msg.type() === 'error') { consoleErrors.push(msg.text()); console.log(' Browser console error:', msg.text()); } }); // Monitor network errors const networkErrors = []; page.on('response', response => { if (response.status() >= 400) { networkErrors.push({ status: response.status(), url: response.url() }); } }); try { console.log('๐Ÿงช Testing Collections Auto-Index Toggle\n'); // Authenticate first console.log('๐Ÿ“ Authenticating...'); await authHelper.ensureAuthenticated(); console.log('โœ… Authenticated\n'); // Test 1: Toggle exists on collections page console.log('Test 1: Toggle exists on collections page'); await page.goto(COLLECTIONS_URL, { waitUntil: 'domcontentloaded', timeout: 30000 }); await delay(2000); // Wait for JS to load and execute const toggleExists = await page.$('#auto-index-toggle'); if (toggleExists) { console.log(' โœ… Toggle element found'); passed++; } else { console.log(' โŒ Toggle element not found'); failed++; } // Test 2: Toggle has associated label with tooltip console.log('\nTest 2: Toggle has label with tooltip'); const labelExists = await page.$('label.ldr-checkbox-label'); const hasTooltip = await page.evaluate(() => { const span = document.querySelector('label.ldr-checkbox-label .ldr-help-tooltip'); return span !== null; }); if (labelExists && hasTooltip) { console.log(' โœ… Label with tooltip exists'); passed++; } else { console.log(` โŒ Label (${!!labelExists}) or tooltip (${hasTooltip}) missing`); failed++; } // Test 3: Toggle loads initial state from API console.log('\nTest 3: Toggle loads state from settings API'); // Get the current setting value directly from API const apiResponse = await page.evaluate(async (key) => { const response = await fetch(`/settings/api/${key}`); return response.json(); }, SETTINGS_API_KEY); const toggleChecked = await page.evaluate(() => { return document.getElementById('auto-index-toggle').checked; }); const expectedChecked = apiResponse.value === true || apiResponse.value === 'true'; if (toggleChecked === expectedChecked) { console.log(` โœ… Toggle state (${toggleChecked}) matches API value (${apiResponse.value})`); passed++; } else { console.log(` โŒ Toggle state (${toggleChecked}) does not match API value (${apiResponse.value})`); failed++; } // Test 4: Toggle change persists to API console.log('\nTest 4: Toggle change persists to API'); const initialState = toggleChecked; // Click the toggle to change state await page.click('#auto-index-toggle'); await delay(1000); // Wait for API call // Verify state changed in UI const newToggleState = await page.evaluate(() => { return document.getElementById('auto-index-toggle').checked; }); // Verify state changed in API const newApiResponse = await page.evaluate(async (key) => { const response = await fetch(`/settings/api/${key}`); return response.json(); }, SETTINGS_API_KEY); const expectedNewState = newApiResponse.value === true || newApiResponse.value === 'true'; if (newToggleState !== initialState && newToggleState === expectedNewState) { console.log(` โœ… Toggle changed from ${initialState} to ${newToggleState} and API reflects this`); passed++; } else { console.log(` โŒ Toggle change failed: UI=${newToggleState}, API=${newApiResponse.value}, expected different from ${initialState}`); failed++; } // Test 5: State persists after page reload console.log('\nTest 5: State persists after page reload'); const stateBeforeReload = newToggleState; await page.reload({ waitUntil: 'domcontentloaded', timeout: 30000 }); await delay(2000); // Wait for JS to load state const stateAfterReload = await page.evaluate(() => { return document.getElementById('auto-index-toggle').checked; }); if (stateAfterReload === stateBeforeReload) { console.log(` โœ… State persisted after reload: ${stateAfterReload}`); passed++; } else { console.log(` โŒ State changed after reload: was ${stateBeforeReload}, now ${stateAfterReload}`); failed++; } // Test 6: Toggle back to original state console.log('\nTest 6: Toggle back to original state'); await page.click('#auto-index-toggle'); await delay(1000); const restoredState = await page.evaluate(() => { return document.getElementById('auto-index-toggle').checked; }); if (restoredState === initialState) { console.log(` โœ… Restored to original state: ${restoredState}`); passed++; } else { console.log(` โŒ Failed to restore: expected ${initialState}, got ${restoredState}`); failed++; } // Test 7: No console or network errors during tests console.log('\nTest 7: No critical errors during tests'); const criticalConsoleErrors = consoleErrors.filter(e => !e.includes('favicon') && !e.includes('404') ); const criticalNetworkErrors = networkErrors.filter(e => !e.url.includes('favicon') && e.status >= 500 ); if (criticalConsoleErrors.length === 0 && criticalNetworkErrors.length === 0) { console.log(' โœ… No critical errors'); passed++; } else { console.log(` โŒ Found errors: ${criticalConsoleErrors.length} console, ${criticalNetworkErrors.length} network`); criticalConsoleErrors.forEach(e => console.log(` Console: ${e}`)); criticalNetworkErrors.forEach(e => console.log(` Network: ${e.status} ${e.url}`)); failed++; } // Take screenshot for debugging await takeScreenshot(page, '/tmp/collections_auto_index_test.png'); } catch (error) { console.error('\nโŒ Test error:', error.message); await takeScreenshot(page, '/tmp/collections_auto_index_error.png'); failed++; } finally { await browser.close(); } // Summary console.log('\n' + '='.repeat(50)); console.log(`๐Ÿ“Š Results: ${passed} passed, ${failed} failed`); console.log('='.repeat(50)); process.exit(failed > 0 ? 1 : 0); } runTests();