/** * Research Workflow Comprehensive Test * Tests the complete research lifecycle from submission to results * CI-compatible: Works in both local and CI environments */ const puppeteer = require('puppeteer'); const AuthHelper = require('./auth_helper'); const { getPuppeteerLaunchOptions } = require('./puppeteer_config'); const { setupDefaultModel } = require('./model_helper'); const fs = require('fs'); const path = require('path'); const BASE_URL = 'http://127.0.0.1:5000'; async function testResearchWorkflow() { const isCI = !!process.env.CI; console.log(`๐Ÿงช Running research workflow test (CI mode: ${isCI})`); // Create screenshots directory const screenshotsDir = path.join(__dirname, 'screenshots'); if (!fs.existsSync(screenshotsDir)) { fs.mkdirSync(screenshotsDir, { recursive: true }); } const browser = await puppeteer.launch(getPuppeteerLaunchOptions()); const page = await browser.newPage(); const auth = new AuthHelper(page, BASE_URL); // Set longer timeout for CI const timeout = isCI ? 90000 : 60000; page.setDefaultTimeout(timeout); page.setDefaultNavigationTimeout(timeout); let testsPassed = 0; let testsFailed = 0; let testsSkipped = 0; try { // Setup: Authenticate console.log('๐Ÿ” Authenticating...'); await auth.ensureAuthenticatedWithTimeout(); console.log('โœ… Authentication successful\n'); // Test 1: Home page loads with research form console.log('๐Ÿ“ Test 1: Research form loads correctly'); try { await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' }); // Check for query input const queryInput = await page.$('#query, input[name="query"]'); if (queryInput) { console.log('โœ… Research form with query input found'); testsPassed++; } else { console.log('โŒ Query input not found'); testsFailed++; } } catch (error) { console.log(`โŒ Test 1 failed: ${error.message}`); testsFailed++; } // Test 2: Model configuration console.log('\n๐Ÿ“ Test 2: Model can be configured'); try { const modelConfigured = await setupDefaultModel(page); if (modelConfigured) { console.log('โœ… Model configuration successful'); testsPassed++; } else { console.log('โš ๏ธ Model configuration may have issues (continuing)'); testsSkipped++; } } catch (error) { console.log(`โš ๏ธ Model configuration error: ${error.message}`); testsSkipped++; } // Test 3: Form fields are accessible console.log('\n๐Ÿ“ Test 3: Form fields are accessible'); try { const formElements = await page.evaluate(() => { return { hasQueryInput: !!document.querySelector('#query, input[name="query"]'), hasSubmitButton: !!document.querySelector('button[type="submit"]'), hasModelSelect: !!document.querySelector('#model, select[name="model"]'), hasSearchEngineSelect: !!document.querySelector('#search_engine, select[name="search_engine"]') }; }); const missingElements = []; if (!formElements.hasQueryInput) missingElements.push('query input'); if (!formElements.hasSubmitButton) missingElements.push('submit button'); if (missingElements.length === 0) { console.log('โœ… All essential form elements found'); testsPassed++; } else { console.log(`โš ๏ธ Missing elements: ${missingElements.join(', ')}`); testsFailed++; } } catch (error) { console.log(`โŒ Test 3 failed: ${error.message}`); testsFailed++; } // Test 4: Query can be entered console.log('\n๐Ÿ“ Test 4: Query input accepts text'); try { await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' }); const queryInput = await page.$('#query, input[name="query"]'); if (queryInput) { // Set value atomically to avoid race with page JS event handlers await page.$eval('#query', (el) => { el.value = 'Test research query for workflow test'; el.dispatchEvent(new Event('input', { bubbles: true })); }); const value = await page.$eval('#query, input[name="query"]', el => el.value); if (value.includes('Test research query')) { console.log('โœ… Query input accepts text correctly'); testsPassed++; } else { console.log(`โŒ Query value mismatch: ${value}`); testsFailed++; } } else { console.log('โŒ Query input not found'); testsFailed++; } } catch (error) { console.log(`โŒ Test 4 failed: ${error.message}`); testsFailed++; } // Test 5: Research submission workflow console.log('\n๐Ÿ“ Test 5: Research submission workflow'); try { await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' }); // Configure model await setupDefaultModel(page); // Enter query await page.waitForSelector('#query', { timeout: 10000 }); await page.type('#query', 'What is machine learning?'); // Take screenshot before submission (skip in CI โ€” diagnostic only) if (!isCI) { await page.screenshot({ path: path.join(screenshotsDir, 'research_workflow_before_submit.png') }); } // Try to submit const submitButton = await page.$('button[type="submit"]'); if (submitButton) { // Click and wait for response try { await Promise.race([ Promise.all([ page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 30000 }), submitButton.click() ]), new Promise((_, reject) => setTimeout(() => reject(new Error('Navigation timeout')), 30000)) ]); } catch { // Check if we're on a research page anyway await new Promise(resolve => setTimeout(resolve, 3000)); } const url = page.url(); console.log(` Current URL after submission: ${url}`); // Take screenshot after submission attempt (skip in CI โ€” diagnostic only) if (!isCI) { await page.screenshot({ path: path.join(screenshotsDir, 'research_workflow_after_submit.png') }); } if (url.includes('/research/') || url.includes('/progress/')) { console.log('โœ… Research submission navigated to research page'); testsPassed++; } else if (url === `${BASE_URL}/` || url.endsWith('/')) { // Still on home page - might be validation error or config issue const errorMsg = await page.evaluate(() => { const alert = document.querySelector('.alert-danger, .error-message'); return alert ? alert.textContent : null; }); if (errorMsg) { console.log(`โš ๏ธ Submission blocked: ${errorMsg.trim()}`); } else { console.log('โš ๏ธ Stayed on home page (may need model configuration)'); } testsSkipped++; } else { console.log(`โš ๏ธ Unexpected URL after submission: ${url}`); testsSkipped++; } } else { console.log('โŒ Submit button not found'); testsFailed++; } } catch (error) { console.log(`โš ๏ธ Test 5 had issues: ${error.message}`); testsSkipped++; } // Test 6: History page shows research history console.log('\n๐Ÿ“ Test 6: History page is accessible'); try { await page.goto(`${BASE_URL}/history/`, { waitUntil: 'domcontentloaded' }); const hasHistoryContent = await page.evaluate(() => { const body = document.body; return body && ( body.innerText.includes('History') || body.innerText.includes('history') || body.innerText.includes('Research') || body.innerText.includes('No research') ); }); if (hasHistoryContent) { console.log('โœ… History page loads correctly'); testsPassed++; } else { console.log('โš ๏ธ History page content unclear'); testsPassed++; } } catch (error) { console.log(`โŒ Test 6 failed: ${error.message}`); testsFailed++; } // Test 7: Navigation between pages works console.log('\n๐Ÿ“ Test 7: Navigation between workflow pages'); try { const pages = [ { path: '/', name: 'Home' }, { path: '/settings/', name: 'Settings' }, { path: '/history/', name: 'History' }, { path: '/metrics/', name: 'Metrics' } ]; let navSuccess = 0; for (const pageInfo of pages) { try { await page.goto(`${BASE_URL}${pageInfo.path}`, { waitUntil: 'domcontentloaded', timeout: 15000 }); // Check we didn't get redirected to login if (!page.url().includes('/auth/login')) { navSuccess++; } } catch { console.log(` โš ๏ธ Navigation to ${pageInfo.name} failed`); } } if (navSuccess >= 3) { console.log(`โœ… Navigation works (${navSuccess}/${pages.length} pages accessible)`); testsPassed++; } else { console.log(`โš ๏ธ Navigation issues (${navSuccess}/${pages.length} pages)`); testsFailed++; } } catch (error) { console.log(`โŒ Test 7 failed: ${error.message}`); testsFailed++; } // Summary console.log('\n' + '='.repeat(50)); console.log('๐Ÿ“Š RESEARCH WORKFLOW TEST SUMMARY'); console.log('='.repeat(50)); console.log(`โœ… Passed: ${testsPassed}`); console.log(`โญ๏ธ Skipped: ${testsSkipped}`); console.log(`โŒ Failed: ${testsFailed}`); console.log(`๐Ÿ“Š Success Rate: ${Math.round((testsPassed / (testsPassed + testsFailed + testsSkipped)) * 100)}%`); // Take final screenshot (skip in CI โ€” diagnostic only) if (!isCI) { await page.screenshot({ path: path.join(screenshotsDir, 'research_workflow_final.png'), fullPage: true }); } if (testsFailed > 0) { console.log('\nโš ๏ธ Too many workflow tests failed'); process.exit(1); } console.log('\n๐ŸŽ‰ Research workflow tests completed!'); process.exit(0); } catch (error) { console.error('\nโŒ Test suite failed:', error.message); // Skip diagnostic screenshot in CI โ€” error context is in the logs if (!isCI) { try { await page.screenshot({ path: path.join(screenshotsDir, `research_workflow_error_${Date.now()}.png`), fullPage: true }); console.log('๐Ÿ“ธ Error screenshot saved'); } catch (screenshotError) { console.log('Could not save screenshot:', screenshotError.message); } } process.exit(1); } finally { await browser.close(); } } // Run the test testResearchWorkflow().catch(error => { console.error('Fatal error:', error); process.exit(1); });