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

345 lines
13 KiB
JavaScript

/**
* Test for toast notifications when saving settings
*
* This test verifies that toast notifications appear when checkbox settings are changed.
*/
const puppeteer = require('puppeteer');
const AuthHelper = require('./auth_helper');
const { getPuppeteerLaunchOptions } = require('./puppeteer_config');
const TEST_CONFIG = {
TIMEOUTS: {
SETTINGS_LOAD: 5000,
TOAST_APPEAR: 5000,
AJAX_SAVE: 3000,
NAVIGATION: 30000
},
URLS: {
BASE: 'http://localhost:5000',
SETTINGS: 'http://localhost:5000/settings'
},
SELECTORS: {
CHECKBOX: 'input[type="checkbox"].ldr-settings-checkbox',
TOAST_CONTAINER: '#toast-container',
TOAST: '.toast, .ldr-toast',
TOAST_VISIBLE: '.toast.visible, .ldr-toast.visible'
}
};
(async () => {
let browser;
let page;
const consoleLogs = [];
const networkRequests = [];
const networkResponses = [];
try {
console.log('=== Testing Toast Notifications ===\n');
browser = await puppeteer.launch(getPuppeteerLaunchOptions());
page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
// Capture ONLY toast-related console logs
page.on('console', msg => {
const text = msg.text();
const type = msg.type();
// Only log toast-related items
if (text.includes('showMessage') ||
text.includes('[Notification]') ||
text.includes('toast') ||
text.includes('Toast') ||
text.includes('[DEBUG]') ||
text.includes('[submitSettingsData]') ||
text.includes('originalSettings cache')) {
consoleLogs.push({ type, text, timestamp: Date.now() });
console.log(`[BROWSER] ${text}`);
}
});
// Capture page errors
page.on('pageerror', error => {
console.error(`[PAGE ERROR] ${error.message}`);
consoleLogs.push({ type: 'pageerror', text: error.message, timestamp: Date.now() });
});
// Capture network requests
page.on('request', req => {
if (req.url().includes('save_all_settings') || req.url().includes('settings')) {
networkRequests.push({
url: req.url(),
method: req.method(),
timestamp: Date.now()
});
}
});
// Capture network responses
page.on('response', async resp => {
if (resp.url().includes('save_all_settings')) {
try {
const text = await resp.text();
networkResponses.push({
url: resp.url(),
status: resp.status(),
body: text.substring(0, 500),
timestamp: Date.now()
});
console.log(`[NETWORK] Response from save_all_settings: ${resp.status()}`);
} catch {
// Response already consumed
}
}
});
// Authenticate with a consistent user
console.log('🔐 Authenticating...');
const authHelper = new AuthHelper(page);
const testUser = 'toast_test_user';
const testPassword = 'T3st!Secure#2024$LDR';
// Try to register/login this specific user
try {
await authHelper.ensureAuthenticated(testUser, testPassword);
} catch {
console.log('Initial auth failed, trying direct registration...');
try {
await authHelper.register(testUser, testPassword);
} catch {
console.log('Registration failed (user may exist), trying login...');
await authHelper.login(testUser, testPassword);
}
}
console.log('✅ Authenticated\n');
// Navigate to settings
console.log('📄 Navigating to settings page...');
await page.goto(TEST_CONFIG.URLS.SETTINGS, {
waitUntil: 'domcontentloaded',
timeout: TEST_CONFIG.TIMEOUTS.NAVIGATION
});
// Check current URL
let currentUrl = page.url();
console.log(`Current URL: ${currentUrl}`);
// Check if we got redirected to login - try login with our test user
if (currentUrl.includes('/auth/login')) {
console.log('⚠️ Redirected to login, attempting login...');
await authHelper.login(testUser, testPassword);
await page.goto(TEST_CONFIG.URLS.SETTINGS, {
waitUntil: 'domcontentloaded',
timeout: TEST_CONFIG.TIMEOUTS.NAVIGATION
});
currentUrl = page.url();
console.log(`After re-login URL: ${currentUrl}`);
}
// Wait longer for settings to load (they load dynamically)
console.log('Waiting for settings page to fully load...');
await new Promise(resolve => setTimeout(resolve, 5000));
// Debug: check page content
const pageDebug = await page.evaluate(() => ({
title: document.title,
url: window.location.href,
allCheckboxes: document.querySelectorAll('input[type="checkbox"]').length,
ldrCheckboxes: document.querySelectorAll('input[type="checkbox"].ldr-settings-checkbox').length,
anyInputs: document.querySelectorAll('input').length,
settingsContent: !!document.getElementById('settings-content'),
bodyText: document.body.textContent.substring(0, 500)
}));
console.log('Page debug info:', JSON.stringify(pageDebug, null, 2));
// Try different selectors
let checkboxSelector = TEST_CONFIG.SELECTORS.CHECKBOX;
let checkboxes = await page.$$(checkboxSelector);
if (checkboxes.length === 0) {
console.log('No ldr-settings-checkbox found, trying any checkbox...');
checkboxSelector = 'input[type="checkbox"]';
checkboxes = await page.$$(checkboxSelector);
}
if (checkboxes.length === 0) {
// Take screenshot for debugging
await page.screenshot({ path: '/tmp/settings_no_checkboxes.png', fullPage: true });
console.log('📸 Screenshot saved to /tmp/settings_no_checkboxes.png');
throw new Error(`No checkboxes found. Page has ${pageDebug.anyInputs} inputs total.`);
}
console.log(`✅ Settings page loaded - found ${checkboxes.length} checkboxes\n`);
// Check if window.ui is available
const uiCheck = await page.evaluate(() => {
return {
windowUi: !!window.ui,
showMessage: !!(window.ui && window.ui.showMessage),
toastContainer: !!document.getElementById('toast-container')
};
});
console.log('UI availability check:', JSON.stringify(uiCheck, null, 2));
// Get first checkbox info
const firstCheckbox = checkboxes[0];
const checkboxInfo = await firstCheckbox.evaluate(el => ({
name: el.name,
checked: el.checked,
id: el.id
}));
console.log('Testing checkbox:', JSON.stringify(checkboxInfo, null, 2));
// Clear console logs before toggling
consoleLogs.length = 0;
networkRequests.length = 0;
networkResponses.length = 0;
console.log('\n🔄 Toggling checkbox...');
const initialState = checkboxInfo.checked;
await firstCheckbox.click();
// Wait for AJAX save to complete
console.log('⏳ Waiting for AJAX save...');
await new Promise(resolve => setTimeout(resolve, TEST_CONFIG.TIMEOUTS.AJAX_SAVE));
// Check checkbox state changed
const newState = await firstCheckbox.evaluate(el => el.checked);
console.log(`Checkbox state: ${initialState}${newState}`);
// Check for toast in DOM
const toastCheck = await page.evaluate(() => {
const container = document.getElementById('toast-container');
const toasts = document.querySelectorAll('.toast, .ldr-toast');
const visibleToasts = document.querySelectorAll('.toast.visible, .ldr-toast.visible');
let containerStyles = null;
if (container) {
const computed = window.getComputedStyle(container);
containerStyles = {
position: computed.position,
top: computed.top,
right: computed.right,
zIndex: computed.zIndex,
display: computed.display,
visibility: computed.visibility
};
}
const toastInfo = [];
toasts.forEach((t, i) => {
const computed = window.getComputedStyle(t);
toastInfo.push({
index: i,
className: t.className,
innerHTML: t.innerHTML.substring(0, 100),
opacity: computed.opacity,
transform: computed.transform,
display: computed.display,
visibility: computed.visibility
});
});
return {
containerExists: !!container,
containerStyles,
totalToasts: toasts.length,
visibleToasts: visibleToasts.length,
toastInfo
};
});
console.log('\n📋 Toast DOM state:');
console.log(JSON.stringify(toastCheck, null, 2));
// Wait a bit more for toast animation
await new Promise(resolve => setTimeout(resolve, 500));
// Check again for visible toasts
const finalToastCheck = await page.evaluate(() => {
const visibleToasts = document.querySelectorAll('.toast.visible, .ldr-toast.visible');
return {
visibleToastsCount: visibleToasts.length,
visibleToastsHTML: Array.from(visibleToasts).map(t => t.innerHTML.substring(0, 100))
};
});
console.log('\n📋 Final toast check:');
console.log(JSON.stringify(finalToastCheck, null, 2));
// Print relevant console logs
console.log('\n📝 Relevant browser console logs:');
const relevantLogs = consoleLogs.filter(log =>
log.text.includes('showMessage') ||
log.text.includes('Notification') ||
log.text.includes('toast') ||
log.text.includes('DEBUG') ||
log.text.includes('submitSettingsData') ||
log.text.includes('originalSettings') ||
log.type === 'error'
);
if (relevantLogs.length === 0) {
console.log(' (No relevant logs captured - this is suspicious!)');
} else {
relevantLogs.forEach(log => {
console.log(` [${log.type}] ${log.text}`);
});
}
// Print network activity
console.log('\n🌐 Network activity:');
console.log(` Requests: ${networkRequests.length}`);
networkRequests.forEach(req => {
console.log(` ${req.method} ${req.url}`);
});
console.log(` Responses: ${networkResponses.length}`);
networkResponses.forEach(resp => {
console.log(` ${resp.status} ${resp.url}`);
if (resp.body) {
console.log(` Body: ${resp.body.substring(0, 200)}`);
}
});
// Take screenshot
const screenshotPath = '/tmp/toast_test_screenshot.png';
await page.screenshot({ path: screenshotPath, fullPage: false });
console.log(`\n📸 Screenshot saved to: ${screenshotPath}`);
// Toggle back to original state
console.log('\n🔄 Restoring original checkbox state...');
await firstCheckbox.click();
await new Promise(resolve => setTimeout(resolve, 1000));
// Final verdict
console.log('\n' + '='.repeat(60));
if (finalToastCheck.visibleToastsCount > 0) {
console.log('✅ SUCCESS: Toast notification appeared!');
} else if (toastCheck.totalToasts > 0) {
console.log('⚠️ PARTIAL: Toast created but not visible (CSS issue)');
} else if (networkResponses.length > 0 && networkResponses[0].status === 200) {
console.log('❌ ISSUE: Save succeeded but no toast was created');
console.log(' Check if notification code is being reached');
} else {
console.log('❌ ISSUE: No save request or toast notification detected');
}
console.log('='.repeat(60));
} catch (error) {
console.error('\n❌ Test failed:', error.message);
console.error(error.stack);
// Print all console logs on failure
console.log('\n📝 All browser console logs:');
consoleLogs.forEach(log => {
console.log(` [${log.type}] ${log.text}`);
});
process.exit(1);
} finally {
if (browser) {
await browser.close();
}
}
})();