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

384 lines
14 KiB
JavaScript

const puppeteer = require('puppeteer');
const AuthHelper = require('./auth_helper');
const fs = require('fs').promises;
const path = require('path');
const { getPuppeteerLaunchOptions } = require('./puppeteer_config');
const BASE_URL = 'http://127.0.0.1:5000';
const TIMEOUT = 120000; // 2 minutes for research to complete
// Color codes for output
const colors = {
info: '\x1b[36m',
success: '\x1b[32m',
warning: '\x1b[33m',
error: '\x1b[31m',
section: '\x1b[34m',
reset: '\x1b[0m'
};
function log(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString();
console.log(`${colors[type]}[${timestamp}] ${message}${colors.reset}`);
}
async function configureSettingsForTest(page) {
log('Configuring settings for faster test...', 'info');
await page.goto(`${BASE_URL}/settings`, { waitUntil: 'domcontentloaded' });
// Set minimal iterations and questions
await page.evaluate(() => {
// Set iterations to 1
const iterationsInput = document.querySelector('input[name="app.research_iterations"]') ||
document.querySelector('#app\\.research_iterations');
if (iterationsInput) {
iterationsInput.value = '1';
iterationsInput.dispatchEvent(new Event('change', { bubbles: true }));
}
// Set questions per iteration to 1
const questionsInput = document.querySelector('input[name="app.questions_per_iteration"]') ||
document.querySelector('#app\\.questions_per_iteration');
if (questionsInput) {
questionsInput.value = '1';
questionsInput.dispatchEvent(new Event('change', { bubbles: true }));
}
// Set direct mode if available
const directModeCheckbox = document.querySelector('input[name="app.direct_mode"]') ||
document.querySelector('#app\\.direct_mode');
if (directModeCheckbox && !directModeCheckbox.checked) {
directModeCheckbox.click();
}
});
// Save settings
const saveButton = await page.$('button[type="submit"]');
if (saveButton) {
await saveButton.click();
await new Promise(r => setTimeout(r, 2000));
}
log('Settings configured', 'success');
}
async function startAndCompleteResearch(page, query) {
log(`Starting research: "${query}"`, 'info');
// Navigate to home page
await page.goto(`${BASE_URL}/`, { waitUntil: 'domcontentloaded' });
// Wait for research form
await page.waitForSelector('#research-form', { visible: true });
// Clear and type research query - use correct selector
const queryInput = await page.$('#query') || await page.$('#research_query') || await page.$('input[name="query"]');
if (queryInput) {
await queryInput.click({ clickCount: 3 }); // Select all
await queryInput.type(query);
} else {
throw new Error('Could not find query input field');
}
// Submit research
await Promise.all([
page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
page.click('#start-research-btn, #submit-research, button[type="submit"]')
]);
// Get research ID from URL
const url = page.url();
const match = url.match(/\/research\/(\d+)/);
const researchId = match ? match[1] : null;
log(`Research started with ID: ${researchId}`, 'success');
// Wait for research to complete or make significant progress
log('Waiting for research to generate metrics...', 'info');
const startTime = Date.now();
const maxWaitTime = 60000; // 1 minute max
let metricsGenerated = false;
// In CI, don't wait long for research
while (Date.now() - startTime < 10000 && !metricsGenerated) { // Only wait 10 seconds
try {
// Check research status with error handling
const status = await page.evaluate(() => {
const statusEl = document.querySelector('.status-text, .research-status, .status');
const progressEl = document.querySelector('.progress-bar, .search-progress');
const searchCount = document.querySelector('.search-count, .searches-completed');
const tokenCount = document.querySelector('.token-count, .tokens-used');
const statusText = statusEl ? statusEl.textContent.toLowerCase() : '';
const hasProgress = Boolean(progressEl);
const hasSearches = Boolean(searchCount);
const hasTokens = Boolean(tokenCount);
return {
text: statusText,
isComplete: statusText.includes('complet') || statusText.includes('finish'),
hasProgress,
hasSearches,
hasTokens,
hasMetrics: hasSearches || hasTokens
};
});
if (status.isComplete) {
log('Research completed!', 'success');
metricsGenerated = true;
break;
}
if (status.hasMetrics) {
log('Research has generated some metrics', 'info');
metricsGenerated = true;
}
// Log progress
if (status.text) {
log(`Status: ${status.text}`, 'info');
}
} catch {
// Page might be reloading
}
await new Promise(r => setTimeout(r, 1000)); // Check more frequently
}
if (!metricsGenerated) {
log('Research is still running, but proceeding to check metrics...', 'warning');
}
return researchId;
}
async function captureMetricsDashboard(page, timestamp) {
log('Navigating to metrics dashboard...', 'info');
// Navigate to metrics page
await page.goto(`${BASE_URL}/metrics`, { waitUntil: 'domcontentloaded' });
// Wait for page to load
await page.waitForSelector('body');
await new Promise(r => setTimeout(r, 3000)); // Give time for data to load
// Capture all metrics data
const metricsData = await page.evaluate(() => {
const data = {
timestamp: new Date().toISOString(),
url: window.location.href,
pageTitle: document.title,
tokenMetrics: {},
searchMetrics: {},
researchMetrics: {},
chartData: {},
rawContent: '',
elements: {}
};
// Get page content
data.rawContent = document.body.innerText.substring(0, 2000);
// Look for token usage metrics
const tokenElements = [
{ selector: '.total-tokens', label: 'Total Tokens' },
{ selector: '.prompt-tokens', label: 'Prompt Tokens' },
{ selector: '.completion-tokens', label: 'Completion Tokens' },
{ selector: '.token-count', label: 'Token Count' },
{ selector: '.usage-value', label: 'Usage Value' },
{ selector: '[data-metric="tokens"]', label: 'Token Metric' }
];
tokenElements.forEach(({ selector, label }) => {
const el = document.querySelector(selector);
if (el) {
data.tokenMetrics[label] = el.textContent.trim();
}
});
// Look for search metrics
const searchElements = [
{ selector: '.search-count', label: 'Search Count' },
{ selector: '.searches-completed', label: 'Searches Completed' },
{ selector: '.search-calls', label: 'Search Calls' },
{ selector: '[data-metric="searches"]', label: 'Search Metric' }
];
searchElements.forEach(({ selector, label }) => {
const el = document.querySelector(selector);
if (el) {
data.searchMetrics[label] = el.textContent.trim();
}
});
// Look for metric cards or stat boxes
const metricCards = document.querySelectorAll('.metric-card, .stat-box, .metric-item, .dashboard-card');
metricCards.forEach(card => {
const title = card.querySelector('.metric-title, .card-title, h3, h4, .label');
const value = card.querySelector('.metric-value, .card-value, .value, .number, span');
if (title && value) {
const key = title.textContent.trim();
const val = value.textContent.trim();
if (key.toLowerCase().includes('token')) {
data.tokenMetrics[key] = val;
} else if (key.toLowerCase().includes('search')) {
data.searchMetrics[key] = val;
} else {
data.researchMetrics[key] = val;
}
}
});
// Look for tables with metrics
const tables = document.querySelectorAll('table');
tables.forEach((table, index) => {
const headers = Array.from(table.querySelectorAll('th')).map(th => th.textContent.trim());
const rows = Array.from(table.querySelectorAll('tbody tr')).map(tr => {
return Array.from(tr.querySelectorAll('td')).map(td => td.textContent.trim());
});
if (headers.length > 0 || rows.length > 0) {
data.chartData[`table_${index}`] = { headers, rows };
}
});
// Look for canvas elements (charts)
const canvases = document.querySelectorAll('canvas');
data.elements.canvasCount = canvases.length;
// Check for Chart.js
data.elements.hasChartJS = typeof Chart !== 'undefined';
return data;
});
// Log findings
log('\n=== METRICS DASHBOARD ANALYSIS ===', 'section');
log('\nToken Metrics Found:', 'info');
if (Object.keys(metricsData.tokenMetrics).length > 0) {
console.log(JSON.stringify(metricsData.tokenMetrics, null, 2));
} else {
log('No token metrics found', 'warning');
}
log('\nSearch Metrics Found:', 'info');
if (Object.keys(metricsData.searchMetrics).length > 0) {
console.log(JSON.stringify(metricsData.searchMetrics, null, 2));
} else {
log('No search metrics found', 'warning');
}
log('\nOther Research Metrics:', 'info');
if (Object.keys(metricsData.researchMetrics).length > 0) {
console.log(JSON.stringify(metricsData.researchMetrics, null, 2));
} else {
log('No other metrics found', 'warning');
}
log('\nPage Elements:', 'info');
console.log(`- Canvas elements: ${metricsData.elements.canvasCount}`);
console.log(`- Chart.js loaded: ${metricsData.elements.hasChartJS}`);
console.log(`- Tables found: ${Object.keys(metricsData.chartData).length}`);
// Show snippet of raw content
log('\nRaw page content preview:', 'info');
console.log(metricsData.rawContent.substring(0, 500) + '...');
// Save full data
const dataPath = path.join(__dirname, `metrics-full-data-${timestamp}.json`);
await fs.writeFile(dataPath, JSON.stringify(metricsData, null, 2));
log(`Full data saved: ${dataPath}`, 'success');
// Determine success
const hasTokenData = Object.keys(metricsData.tokenMetrics).length > 0;
const hasSearchData = Object.keys(metricsData.searchMetrics).length > 0;
const hasAnyData = hasTokenData || hasSearchData || Object.keys(metricsData.researchMetrics).length > 0;
return { hasTokenData, hasSearchData, hasAnyData, metricsData };
}
async function testMetricsFullFlow() {
const browser = await puppeteer.launch(getPuppeteerLaunchOptions());
const page = await browser.newPage();
page.setDefaultTimeout(TIMEOUT);
// Log console errors
page.on('console', msg => {
if (msg.type() === 'error') {
log(`Browser error: ${msg.text()}`, 'error');
}
});
try {
// Create test user with special characters in password
const timestamp = Date.now();
const username = `metrics_${timestamp}`;
const password = `test'pass"with$pecial`;
log('\n=== FULL METRICS FLOW TEST ===', 'section');
log(`Username: ${username}`, 'info');
log(`Password: ${password} (with special characters)`, 'info');
// Step 1: Register/Login
log('\n=== AUTHENTICATION ===', 'section');
const auth = new AuthHelper(page, BASE_URL);
await auth.register(username, password);
// Step 2: Configure settings for fast test
log('\n=== CONFIGURING SETTINGS ===', 'section');
await configureSettingsForTest(page);
// Step 3: Run a research
log('\n=== RUNNING RESEARCH ===', 'section');
const researchId = await startAndCompleteResearch(page, "What are the top 3 benefits of renewable energy?");
// Step 4: Navigate around to ensure metrics are saved
log('\n=== GENERATING ADDITIONAL ACTIVITY ===', 'section');
// Visit history
await page.goto(`${BASE_URL}/history`, { waitUntil: 'domcontentloaded' });
log('Visited history page', 'success');
await new Promise(r => setTimeout(r, 2000));
// Go back to research
if (researchId) {
await page.goto(`${BASE_URL}/research/${researchId}`, { waitUntil: 'domcontentloaded' });
log('Returned to research page', 'success');
await new Promise(r => setTimeout(r, 2000));
}
// Step 5: Check metrics dashboard
log('\n=== CHECKING METRICS DASHBOARD ===', 'section');
const { hasTokenData, hasSearchData, hasAnyData } = await captureMetricsDashboard(page, timestamp);
// Final verdict
log('\n=== TEST RESULTS ===', 'section');
if (hasTokenData && hasSearchData) {
log('✅ METRICS DASHBOARD IS FULLY WORKING!', 'success');
log('✅ Both token and search metrics are displayed', 'success');
} else if (hasAnyData) {
log('⚠️ METRICS DASHBOARD IS PARTIALLY WORKING', 'warning');
log(`Token metrics: ${hasTokenData ? '✅' : '❌'}`, hasTokenData ? 'success' : 'error');
log(`Search metrics: ${hasSearchData ? '✅' : '❌'}`, hasSearchData ? 'success' : 'error');
} else {
log('❌ METRICS DASHBOARD IS NOT SHOWING ANY DATA', 'error');
}
} catch (error) {
log(`\n❌ Test failed: ${error.message}`, 'error');
console.error(error);
} finally {
// Close browser properly
await browser.close();
}
}
// Run the test
testMetricsFullFlow().catch(console.error);