7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
330 lines
13 KiB
JavaScript
330 lines
13 KiB
JavaScript
/**
|
|
* 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);
|
|
});
|