Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

266 lines
9.8 KiB
JavaScript

/**
* Error Recovery UI Test
* Tests how the UI handles various error conditions gracefully
* CI-compatible: Works in both local and CI environments
*/
const puppeteer = require('puppeteer');
const AuthHelper = require('./auth_helper');
const { getPuppeteerLaunchOptions } = require('./puppeteer_config');
const fs = require('fs');
const path = require('path');
const BASE_URL = 'http://127.0.0.1:5000';
async function testErrorRecovery() {
const isCI = !!process.env.CI;
console.log(`🧪 Running error recovery 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 ? 60000 : 30000;
page.setDefaultTimeout(timeout);
page.setDefaultNavigationTimeout(timeout);
let testsPassed = 0;
let testsFailed = 0;
// Track console errors
const consoleErrors = [];
page.on('console', msg => {
if (msg.type() === 'error') {
consoleErrors.push(msg.text());
}
});
try {
// Setup: Authenticate
console.log('🔐 Authenticating...');
await auth.ensureAuthenticatedWithTimeout();
console.log('✅ Authentication successful\n');
// Test 1: Invalid research query (empty)
console.log('📝 Test 1: Empty query validation');
try {
await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' });
// Try to submit empty query
const submitButton = await page.$('button[type="submit"]');
if (submitButton) {
await submitButton.click();
await new Promise(resolve => setTimeout(resolve, 1000));
// Check that we're still on the same page (form validation prevented submission)
const currentUrl = page.url();
if (currentUrl === `${BASE_URL}/` || currentUrl.includes('/auth/')) {
console.log('✅ Empty query was properly rejected');
testsPassed++;
} else {
console.log('⚠️ Form submitted with empty query (may be expected)');
testsPassed++; // Some forms may allow this
}
} else {
console.log('⚠️ Submit button not found');
testsPassed++;
}
} catch (error) {
console.log(`❌ Test 1 failed: ${error.message}`);
testsFailed++;
}
// Test 2: Navigate to non-existent research ID
console.log('\n📝 Test 2: Non-existent research page');
try {
await page.goto(`${BASE_URL}/research/results/nonexistent-id-99999`, {
waitUntil: 'domcontentloaded',
timeout: 30000
});
// Should show error message or redirect
const pageContent = await page.content();
const hasErrorMessage = pageContent.includes('not found') ||
pageContent.includes('error') ||
pageContent.includes('Error') ||
pageContent.includes('404') ||
page.url().includes('/auth/') ||
page.url() === `${BASE_URL}/`;
if (hasErrorMessage) {
console.log('✅ Invalid research ID handled gracefully');
testsPassed++;
} else {
console.log('⚠️ Page loaded without error indication');
testsPassed++; // May still be valid behavior
}
} catch (error) {
console.log(`✅ Invalid research ID caused expected error: ${error.message}`);
testsPassed++; // Error is expected behavior
}
// Test 3: Settings page error handling
console.log('\n📝 Test 3: Settings page loads without JS errors');
try {
const preErrorCount = consoleErrors.length;
await page.goto(`${BASE_URL}/settings/`, { waitUntil: 'domcontentloaded' });
// Wait for page to stabilize
await new Promise(resolve => setTimeout(resolve, 2000));
// Check for critical JS errors (not all errors are bad)
const newErrors = consoleErrors.slice(preErrorCount);
const criticalErrors = newErrors.filter(e =>
e.includes('Uncaught') ||
e.includes('TypeError') ||
e.includes('ReferenceError')
);
if (criticalErrors.length === 0) {
console.log('✅ Settings page loads without critical JS errors');
testsPassed++;
} else {
console.log(`⚠️ Settings page has ${criticalErrors.length} critical errors`);
criticalErrors.forEach(e => console.log(` - ${e}`));
testsFailed++;
}
} catch (error) {
console.log(`❌ Test 3 failed: ${error.message}`);
testsFailed++;
}
// Test 4: Metrics page graceful degradation
console.log('\n📝 Test 4: Metrics page handles no data gracefully');
try {
await page.goto(`${BASE_URL}/metrics/`, { waitUntil: 'domcontentloaded' });
// Page should load even with no metrics data
const hasMetricsContent = await page.evaluate(() => {
const body = document.body;
return body && body.innerText.length > 100;
});
if (hasMetricsContent) {
console.log('✅ Metrics page loads gracefully');
testsPassed++;
} else {
console.log('⚠️ Metrics page appears empty but loaded');
testsPassed++; // Still valid
}
} catch (error) {
console.log(`❌ Test 4 failed: ${error.message}`);
testsFailed++;
}
// Test 5: History page with no history
console.log('\n📝 Test 5: History page handles empty history');
try {
await page.goto(`${BASE_URL}/history/`, { waitUntil: 'domcontentloaded' });
// Should show empty state or message
const pageContent = await page.content();
const hasContent = pageContent.includes('history') ||
pageContent.includes('History') ||
pageContent.includes('research') ||
pageContent.includes('No ') ||
pageContent.includes('empty');
if (hasContent) {
console.log('✅ History page handles empty state gracefully');
testsPassed++;
} else {
console.log('⚠️ History page content unclear');
testsPassed++;
}
} catch (error) {
console.log(`❌ Test 5 failed: ${error.message}`);
testsFailed++;
}
// Test 6: API error simulation
console.log('\n📝 Test 6: UI handles API errors');
try {
await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' });
// Monitor for failed requests
let failedRequests = 0;
page.on('requestfailed', () => failedRequests++);
// Try to trigger an API call (visit settings)
await page.goto(`${BASE_URL}/settings/`, { waitUntil: 'domcontentloaded' });
await new Promise(resolve => setTimeout(resolve, 2000));
// Check page is still usable
const isPageUsable = await page.evaluate(() => {
return document.body !== null && document.body.innerHTML.length > 0;
});
if (isPageUsable) {
console.log(`✅ UI remains usable (${failedRequests} failed requests handled)`);
testsPassed++;
} else {
console.log('❌ UI became unusable after errors');
testsFailed++;
}
} catch (error) {
console.log(`❌ Test 6 failed: ${error.message}`);
testsFailed++;
}
// Summary
console.log('\n' + '='.repeat(50));
console.log('📊 ERROR RECOVERY TEST SUMMARY');
console.log('='.repeat(50));
console.log(`✅ Passed: ${testsPassed}`);
console.log(`❌ Failed: ${testsFailed}`);
console.log(`📊 Success Rate: ${Math.round((testsPassed / (testsPassed + testsFailed)) * 100)}%`);
if (testsFailed > 0) {
console.log('\n⚠️ Some error recovery tests failed');
if (!isCI) {
await page.screenshot({
path: path.join(screenshotsDir, 'error_recovery_final.png'),
fullPage: true
});
}
process.exit(1);
}
console.log('\n🎉 All error recovery tests passed!');
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, `error_recovery_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
testErrorRecovery().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});