mirror of
https://github.com/LearningCircuit/local-deep-research.git
synced 2026-06-15 19:46:56 +03:00
fix(playwright): replace networkidle with domcontentloaded and preserve mobile nav
- Replace all networkidle waits with domcontentloaded + 500ms timeout
to prevent Chart.js and API-related timeouts across all tests
- Add preserveMobileNav option to ensureSheetsClosed helper function
to allow mobile nav tests to keep nav in DOM for visibility checks
- Update mobile nav tests to use { preserveMobileNav: true } option
- Add new iPhone 14/Pro Max snapshot baselines generated from test runs
This commit is contained in:
@@ -46,34 +46,44 @@ const PAGES = [
|
||||
* even when hidden with CSS transforms. We must completely remove it from the DOM.
|
||||
*
|
||||
* @param {import('@playwright/test').Page} page - Playwright page object
|
||||
* @param {Object} options - Options for sheet removal
|
||||
* @param {boolean} options.preserveMobileNav - If true, keep mobile nav in DOM (default: false)
|
||||
*/
|
||||
async function ensureSheetsClosed(page) {
|
||||
async function ensureSheetsClosed(page, options = {}) {
|
||||
const { preserveMobileNav = false } = options;
|
||||
|
||||
// Remove sheet elements from DOM
|
||||
await page.evaluate(() => {
|
||||
await page.evaluate((preserveNav) => {
|
||||
// Remove ALL sheet-related elements (use broader selectors)
|
||||
const selectors = [
|
||||
'.ldr-mobile-sheet-menu',
|
||||
'.ldr-mobile-sheet-overlay',
|
||||
'.ldr-mobile-sheet',
|
||||
'.ldr-mobile-bottom-nav', // Also hide mobile nav on tablets
|
||||
'[class*="sheet"]:not([class*="stylesheet"])',
|
||||
'[class*="drawer"]',
|
||||
'[role="dialog"]:not(.ldr-mode-selection)'
|
||||
];
|
||||
|
||||
// Only remove mobile nav if not preserving it
|
||||
if (!preserveNav) {
|
||||
selectors.push('.ldr-mobile-bottom-nav');
|
||||
}
|
||||
|
||||
selectors.forEach(selector => {
|
||||
document.querySelectorAll(selector).forEach(el => {
|
||||
el.remove();
|
||||
});
|
||||
});
|
||||
|
||||
// Clear MobileNavigation references
|
||||
// Clear MobileNavigation references (but preserve nav reference if needed)
|
||||
if (window.mobileNav && window.mobileNav.elements) {
|
||||
window.mobileNav.elements.sheet = null;
|
||||
window.mobileNav.elements.overlay = null;
|
||||
window.mobileNav.elements.nav = null;
|
||||
if (!preserveNav) {
|
||||
window.mobileNav.elements.nav = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, preserveMobileNav);
|
||||
|
||||
// Force reflow
|
||||
await page.evaluate(() => document.body.offsetHeight);
|
||||
|
||||
@@ -35,8 +35,9 @@ test.describe('Mobile Navigation Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await ensureSheetsClosed(page);
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await ensureSheetsClosed(page, { preserveMobileNav: true });
|
||||
|
||||
const nav = page.locator('.ldr-mobile-bottom-nav');
|
||||
await expect(nav).toBeVisible();
|
||||
@@ -60,8 +61,9 @@ test.describe('Mobile Navigation Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await ensureSheetsClosed(page);
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await ensureSheetsClosed(page, { preserveMobileNav: true });
|
||||
|
||||
// Find and click the More button
|
||||
const moreBtn = page.locator('[data-nav="more"], .ldr-nav-more-btn, .ldr-mobile-bottom-nav button:has-text("More")');
|
||||
@@ -99,7 +101,8 @@ test.describe('Mobile Navigation Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const navItems = page.locator('.ldr-mobile-bottom-nav a, .ldr-mobile-bottom-nav button');
|
||||
const count = await navItems.count();
|
||||
@@ -128,7 +131,8 @@ test.describe('Form Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const textarea = page.locator('#query, textarea[name="query"], .ldr-research-textarea');
|
||||
if (await textarea.count() > 0) {
|
||||
@@ -145,7 +149,8 @@ test.describe('Form Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const startBtn = page.locator('#start-research-btn, button:has-text("Start Research")');
|
||||
if (await startBtn.count() > 0) {
|
||||
@@ -162,7 +167,8 @@ test.describe('Form Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const modeSelection = page.locator('.ldr-mode-selection');
|
||||
if (await modeSelection.count() > 0) {
|
||||
@@ -179,7 +185,8 @@ test.describe('Form Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/settings/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForSelector('.ldr-loading-spinner', { state: 'hidden', timeout: 10000 }).catch(() => {});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
@@ -202,7 +209,8 @@ test.describe('Form Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/settings/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForSelector('.ldr-loading-spinner', { state: 'hidden', timeout: 10000 }).catch(() => {});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
@@ -225,7 +233,8 @@ test.describe('Form Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/settings/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForSelector('.ldr-loading-spinner', { state: 'hidden', timeout: 10000 }).catch(() => {});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
@@ -254,7 +263,8 @@ test.describe('Card Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/history/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Look for empty state message or card container
|
||||
const emptyState = page.locator('.ldr-empty-state, [class*="empty"], :text("No research history")');
|
||||
@@ -282,7 +292,8 @@ test.describe('Card Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/library/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Look for empty state or document list
|
||||
const emptyState = page.locator('.ldr-empty-state, [class*="empty"], :text("No documents")');
|
||||
@@ -309,7 +320,8 @@ test.describe('Card Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/metrics/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const metricLink = page.locator('.ldr-metric-link').first();
|
||||
@@ -333,7 +345,8 @@ test.describe('News Page Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/news/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Look for template cards
|
||||
const template = page.locator('[class*="template"], .news-template, .ldr-subscription-template').first();
|
||||
@@ -351,7 +364,8 @@ test.describe('News Page Components', () => {
|
||||
}
|
||||
|
||||
await page.goto('/news/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const createBtn = page.locator('button, a').filter({
|
||||
hasText: /Create.*Subscription/i,
|
||||
@@ -377,7 +391,8 @@ test.describe('Interaction States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const toggle = page.locator('.ldr-advanced-options-toggle').first();
|
||||
if ((await toggle.count()) === 0 || !(await toggle.isVisible())) {
|
||||
@@ -404,7 +419,8 @@ test.describe('Interaction States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const toggle = page.locator('.ldr-advanced-options-toggle').first();
|
||||
if ((await toggle.count()) === 0 || !(await toggle.isVisible())) {
|
||||
@@ -437,7 +453,8 @@ test.describe('Interaction States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/settings/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await page.waitForSelector('.ldr-loading-spinner', { state: 'hidden', timeout: 10000 }).catch(() => {});
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
@@ -459,7 +476,8 @@ test.describe('Interaction States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const textarea = page.locator('#query, textarea[name="query"], .ldr-research-textarea').first();
|
||||
if (await textarea.isVisible()) {
|
||||
@@ -496,8 +514,9 @@ test.describe('Interaction States', () => {
|
||||
|
||||
for (const pageInfo of pages) {
|
||||
await page.goto(pageInfo.path);
|
||||
await page.waitForLoadState('networkidle');
|
||||
await ensureSheetsClosed(page);
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await ensureSheetsClosed(page, { preserveMobileNav: true });
|
||||
|
||||
const nav = page.locator('.ldr-mobile-bottom-nav');
|
||||
if (await nav.isVisible()) {
|
||||
@@ -545,7 +564,8 @@ test.describe('Loading and Error States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Inject an error message element for testing
|
||||
await page.evaluate(() => {
|
||||
@@ -574,7 +594,8 @@ test.describe('Loading and Error States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/history/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await ensureSheetsClosed(page);
|
||||
|
||||
// Check for empty state message
|
||||
@@ -594,7 +615,8 @@ test.describe('Loading and Error States', () => {
|
||||
}
|
||||
|
||||
await page.goto('/library/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await ensureSheetsClosed(page);
|
||||
|
||||
// Check for empty state message
|
||||
@@ -791,7 +813,8 @@ test.describe('Page Headers', () => {
|
||||
}
|
||||
|
||||
await page.goto(pageInfo.path);
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForTimeout(500);
|
||||
await ensureSheetsClosed(page);
|
||||
|
||||
// Find the main header/title area
|
||||
|
||||
Reference in New Issue
Block a user