fix(ci): eliminate remaining networkidle2 hangs and fix research submit test
- auth_helper.js: replace 4 networkidle2 references in logout() with domcontentloaded to prevent hang when WebSocket/polling keeps network active - test_research_submit.js: fix race condition where setupDefaultModel() triggers provider-change JS that asynchronously clears the query field; add 1.5s settle delay and verify query text persists before submitting - test_research_form_ci.js: remove unused formSubmitWithQuery and enterKeySubmits methods (dead code never called in main())
This commit is contained in:
@@ -720,7 +720,7 @@ class AuthHelper {
|
||||
if (logoutForm) {
|
||||
await Promise.all([
|
||||
this.page.waitForNavigation({
|
||||
waitUntil: 'networkidle2',
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: AUTH_CONFIG.timeouts.logout
|
||||
}).catch(() => {}),
|
||||
this.page.evaluate(() => {
|
||||
@@ -732,7 +732,7 @@ class AuthHelper {
|
||||
if (logoutLink) {
|
||||
await Promise.all([
|
||||
this.page.waitForNavigation({
|
||||
waitUntil: 'networkidle2',
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: AUTH_CONFIG.timeouts.logout
|
||||
}).catch(() => {}),
|
||||
this.page.click('a.logout-btn')
|
||||
@@ -740,7 +740,7 @@ class AuthHelper {
|
||||
} else {
|
||||
// Last resort: navigate directly to logout URL
|
||||
await this.page.goto(`${this.page.url().split('/').slice(0, 3).join('/')}${AUTH_CONFIG.paths.logout}`, {
|
||||
waitUntil: 'networkidle2',
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: AUTH_CONFIG.timeouts.logout
|
||||
});
|
||||
}
|
||||
@@ -756,7 +756,7 @@ class AuthHelper {
|
||||
} else {
|
||||
// Double-check by trying to access a protected page
|
||||
await this.page.goto(`${this.page.url().split('/').slice(0, 3).join('/')}/settings/`, {
|
||||
waitUntil: 'networkidle2',
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: AUTH_CONFIG.timeouts.formSelector
|
||||
}).catch(() => {});
|
||||
|
||||
|
||||
@@ -573,54 +573,6 @@ const AdvancedOptionsTests = {
|
||||
// Form Submission Tests
|
||||
// ============================================================================
|
||||
const FormSubmissionTests = {
|
||||
async formSubmitWithQuery(page, baseUrl) {
|
||||
await navigateTo(page, `${baseUrl}/`);
|
||||
|
||||
// Fill in query
|
||||
await page.evaluate(() => {
|
||||
const queryInput = document.querySelector('textarea[name="query"], input[name="query"], #query');
|
||||
if (queryInput) {
|
||||
queryInput.value = 'What is the capital of France?';
|
||||
queryInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
});
|
||||
|
||||
// Submit form
|
||||
const [response] = await Promise.all([
|
||||
page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: 10000 }).catch(() => null),
|
||||
page.click('button[type="submit"], .start-research, #start-research').catch(() => {})
|
||||
]);
|
||||
|
||||
await delay(1000);
|
||||
|
||||
const result = await page.evaluate(() => {
|
||||
const url = window.location.href;
|
||||
const hasProgress = url.includes('progress') || url.includes('research');
|
||||
const hasResearchId = /\/research\/|\/progress\/|research_id=/.test(url);
|
||||
|
||||
// Check for progress indicators
|
||||
const progressBar = document.querySelector('.progress, .progress-bar, [role="progressbar"]');
|
||||
const statusText = document.querySelector('.status, .research-status, [class*="status"]');
|
||||
|
||||
return {
|
||||
url,
|
||||
hasProgress,
|
||||
hasResearchId,
|
||||
hasProgressBar: !!progressBar,
|
||||
hasStatusText: !!statusText
|
||||
};
|
||||
});
|
||||
|
||||
const submitted = result.hasProgress || result.hasResearchId || result.hasProgressBar;
|
||||
|
||||
return {
|
||||
passed: submitted,
|
||||
message: submitted
|
||||
? `Form submitted successfully (url contains progress/research: ${result.hasProgress}, has progress bar: ${result.hasProgressBar})`
|
||||
: `Form submission unclear (stayed at: ${result.url})`
|
||||
};
|
||||
},
|
||||
|
||||
async formValidationEmptyQuery(page, baseUrl) {
|
||||
await navigateTo(page, `${baseUrl}/`);
|
||||
await delay(500);
|
||||
@@ -682,51 +634,6 @@ const FormSubmissionTests = {
|
||||
// Keyboard Interaction Tests
|
||||
// ============================================================================
|
||||
const KeyboardTests = {
|
||||
async enterKeySubmits(page, baseUrl) {
|
||||
await navigateTo(page, `${baseUrl}/`);
|
||||
|
||||
// Focus query input and type
|
||||
const queryInput = await page.$('textarea[name="query"], input[name="query"], #query');
|
||||
if (!queryInput) {
|
||||
return { passed: null, skipped: true, message: 'Query input not found' };
|
||||
}
|
||||
|
||||
await queryInput.type('Test query for keyboard submission');
|
||||
|
||||
// Check if textarea or input (behavior differs)
|
||||
const isTextarea = await page.evaluate(() => {
|
||||
const input = document.querySelector('textarea[name="query"], input[name="query"], #query');
|
||||
return input?.tagName?.toLowerCase() === 'textarea';
|
||||
});
|
||||
|
||||
if (isTextarea) {
|
||||
// For textarea, Ctrl+Enter should submit
|
||||
await page.keyboard.down('Control');
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.up('Control');
|
||||
} else {
|
||||
// For input, Enter should submit
|
||||
await page.keyboard.press('Enter');
|
||||
}
|
||||
|
||||
await delay(1000);
|
||||
|
||||
const result = await page.evaluate(() => {
|
||||
const url = window.location.href;
|
||||
return {
|
||||
url,
|
||||
navigated: url.includes('progress') || url.includes('research')
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
passed: result.navigated,
|
||||
message: result.navigated
|
||||
? `${isTextarea ? 'Ctrl+Enter' : 'Enter'} key submits form`
|
||||
: `Keyboard submit did not navigate (url: ${result.url})`
|
||||
};
|
||||
},
|
||||
|
||||
async shiftEnterNewline(page, baseUrl) {
|
||||
// Shift+Enter newline behavior is unreliable in headless Chrome
|
||||
if (process.env.CI) {
|
||||
|
||||
@@ -20,14 +20,6 @@ const path = require('path');
|
||||
let browser;
|
||||
const isCI = !!process.env.CI;
|
||||
|
||||
// Skip in CI: setupDefaultModel() triggers provider-change JS that clears the
|
||||
// query field, causing the form to submit empty and redirect to /. This is a
|
||||
// test-infrastructure issue, not a product bug.
|
||||
if (isCI) {
|
||||
console.log('⏭️ Skipping research submit test in CI (known model-setup race condition)');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.log(`🧪 Running research submit test (CI mode: ${isCI})`);
|
||||
// Create screenshots directory
|
||||
const screenshotsDir = path.join(__dirname, 'screenshots');
|
||||
@@ -62,16 +54,30 @@ const path = require('path');
|
||||
await page.goto('http://127.0.0.1:5000/', { waitUntil: 'domcontentloaded' });
|
||||
|
||||
// Set up model configuration
|
||||
// NOTE: setupDefaultModel() calls page.select('#model_provider') which
|
||||
// triggers frontend JS that asynchronously resets the form (including
|
||||
// clearing the query field). We must wait for that to settle before typing.
|
||||
console.log('🔧 Configuring model...');
|
||||
const modelConfigured = await setupDefaultModel(page);
|
||||
if (!modelConfigured) {
|
||||
throw new Error('Failed to configure model');
|
||||
}
|
||||
|
||||
// Wait for provider-change JS to finish resetting the form
|
||||
await new Promise(resolve => setTimeout(resolve, 1500));
|
||||
|
||||
// Wait for and fill the query field
|
||||
await page.waitForSelector('#query', { timeout: 10000 });
|
||||
// Clear any residual value then type
|
||||
await page.$eval('#query', el => { el.value = ''; });
|
||||
await page.type('#query', 'What is Node.js?');
|
||||
console.log('✅ Query entered');
|
||||
|
||||
// Verify the query actually stuck (guard against async clear)
|
||||
const queryValue = await page.$eval('#query', el => el.value);
|
||||
if (!queryValue || queryValue.trim() === '') {
|
||||
throw new Error('Query field was cleared after typing — provider-change race condition');
|
||||
}
|
||||
console.log('✅ Query entered:', queryValue);
|
||||
|
||||
// Check if model and search engine are pre-selected
|
||||
const formValues = await page.evaluate(() => {
|
||||
|
||||
Reference in New Issue
Block a user