/** * Navigation Manager * * Handles page navigation with error handling, retries, and logging */ class NavigationManager { constructor(coreParser) { this.coreParser = coreParser; } /** * Navigate to URL with comprehensive error handling */ async navigateTo(url, options = {}) { const { pageId = "default", waitUntil = "domcontentloaded", retries = 1, retryDelay = 2000, timeout = this.coreParser.config.timeout, } = options; const page = this.coreParser.getPage(pageId); if (!page) { throw new Error(`Page with ID '${pageId}' not found`); } let lastError; for (let attempt = 0; attempt <= retries; attempt++) { try { console.log( `🌐 Navigating to: ${url} (attempt ${attempt + 1}/${retries + 1})` ); await page.goto(url, { waitUntil, timeout, }); console.log(`✅ Navigation successful: ${url}`); return true; } catch (error) { lastError = error; console.warn( `⚠️ Navigation attempt ${attempt + 1} failed: ${error.message}` ); if (attempt < retries) { console.log(`🔄 Retrying in ${retryDelay}ms...`); await this.delay(retryDelay); } } } // All attempts failed const errorMessage = `Navigation failed after ${retries + 1} attempts: ${ lastError.message }`; console.error(`❌ ${errorMessage}`); throw new Error(errorMessage); } /** * Navigate and wait for specific selector */ async navigateAndWaitFor(url, selector, options = {}) { await this.navigateTo(url, options); const { pageId = "default", timeout = this.coreParser.config.timeout } = options; const page = this.coreParser.getPage(pageId); try { await page.waitForSelector(selector, { timeout }); console.log(`✅ Selector found: ${selector}`); return true; } catch (error) { console.warn(`⚠️ Selector not found: ${selector} - ${error.message}`); return false; } } /** * Check if current page has specific content */ async hasContent(content, options = {}) { const { pageId = "default", timeout = 5000 } = options; const page = this.coreParser.getPage(pageId); try { await page.waitForFunction( (text) => document.body.innerText.includes(text), content, { timeout } ); return true; } catch { return false; } } /** * Utility delay function */ async delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Get current page URL */ getCurrentUrl(pageId = "default") { const page = this.coreParser.getPage(pageId); return page ? page.url() : null; } /** * Take screenshot for debugging */ async screenshot(filepath, pageId = "default") { const page = this.coreParser.getPage(pageId); if (page) { await page.screenshot({ path: filepath }); console.log(`📸 Screenshot saved: ${filepath}`); } } } module.exports = NavigationManager;