How does Puppeteer handle errors and debugging? What are the common debugging techniques and tools?
Puppeteer provides various error handling and debugging techniques to help developers quickly identify and resolve issues, improving development efficiency.
1. Basic Error Handling
try-catch Pattern:
javascriptconst puppeteer = require('puppeteer'); async function safeExecution() { const browser = await puppeteer.launch(); const page = await browser.newPage(); try { await page.goto('https://example.com'); await page.click('#button'); } catch (error) { console.error('Error occurred:', error.message); // Error handling logic } finally { await browser.close(); } } safeExecution();
Timeout Handling:
javascripttry { await page.goto('https://example.com', { timeout: 5000 }); } catch (error) { if (error.name === 'TimeoutError') { console.log('Page load timeout'); } }
2. Debug Mode
Enable Debug Mode:
javascript// Method 1: Use headless: false const browser = await puppeteer.launch({ headless: false, slowMo: 100 // Slow down operations }); // Method 2: Use devtools const browser = await puppeteer.launch({ headless: false, devtools: true });
Use slowMo:
javascriptconst browser = await puppeteer.launch({ headless: false, slowMo: 50 // Delay each operation by 50ms });
3. Logging
Console Logging:
javascriptpage.on('console', msg => { console.log('Browser console:', msg.text()); }); // Capture different types of logs page.on('console', msg => { const type = msg.type(); const text = msg.text(); if (type === 'error') { console.error('Browser error:', text); } else if (type === 'warning') { console.warn('Browser warning:', text); } else { console.log('Browser log:', text); } });
Page Error Logging:
javascriptpage.on('pageerror', error => { console.error('Page error:', error.message); });
Request Failure Logging:
javascriptpage.on('requestfailed', request => { console.log('Request failed:', request.url()); console.log('Failure:', request.failure()); });
4. Screenshots and Video Recording
Screenshot on Error:
javascriptasync function withErrorScreenshot(page, operation) { try { await operation(); } catch (error) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); await page.screenshot({ path: `error-${timestamp}.png`, fullPage: true }); throw error; } } // Usage example await withErrorScreenshot(page, async () => { await page.goto('https://example.com'); await page.click('#button'); });
Video Recording:
javascriptconst { spawn } = require('child_process'); async function recordVideo(page, outputPath, operation) { // Use ffmpeg to record screen const ffmpeg = spawn('ffmpeg', [ '-f', 'x11grab', '-r', '30', '-s', '1920x1080', '-i', ':99', '-c:v', 'libx264', '-preset', 'ultrafast', outputPath ]); try { await operation(); } finally { ffmpeg.kill('SIGINT'); } }
5. Network Debugging
Monitor Network Requests:
javascriptpage.on('request', request => { console.log('Request:', request.url()); }); page.on('response', response => { console.log('Response:', response.url(), response.status()); }); page.on('requestfinished', request => { console.log('Request finished:', request.url()); });
Capture Request and Response Data:
javascriptconst requests = []; page.on('request', request => { requests.push({ url: request.url(), method: request.method(), headers: request.headers() }); }); page.on('response', async response => { const request = requests.find(r => r.url === response.url()); if (request) { request.status = response.status(); request.headers = response.headers(); try { request.body = await response.text(); } catch (error) { request.body = null; } } });
6. Performance Tracing
Enable Performance Tracing:
javascriptconst client = await page.target().createCDPSession(); await client.send('Performance.enable'); await client.send('Network.enable'); // Get performance metrics const metrics = await client.send('Performance.getMetrics'); console.log('Performance metrics:', metrics);
Trace Timeline:
javascriptawait page.tracing.start({ path: 'trace.json' }); // Execute operations await page.goto('https://example.com'); await page.tracing.stop();
7. Element Debugging
Highlight Element:
javascriptasync function highlightElement(page, selector) { await page.evaluate(selector => { const element = document.querySelector(selector); if (element) { element.style.border = '3px solid red'; element.style.backgroundColor = 'yellow'; } }, selector); }
Check Element State:
javascriptasync function checkElement(page, selector) { const isVisible = await page.isVisible(selector); const isEnabled = await page.isDisabled(selector); const isClickable = await page.isClickable(selector); console.log('Element state:', { selector, isVisible, isEnabled, isClickable }); }
Get Element Position:
javascriptconst position = await page.evaluate(selector => { const element = document.querySelector(selector); if (element) { const rect = element.getBoundingClientRect(); return { x: rect.left, y: rect.top, width: rect.width, height: rect.height }; } }, '.element');
8. Debugging Utility Functions
Wait and Debug:
javascriptasync function waitForAndDebug(page, selector, options = {}) { console.log(`Waiting for selector: ${selector}`); try { await page.waitForSelector(selector, { timeout: options.timeout || 30000, visible: options.visible !== false }); console.log(`Found selector: ${selector}`); } catch (error) { console.error(`Failed to find selector: ${selector}`); await page.screenshot({ path: 'debug-failed.png' }); throw error; } }
Click and Debug:
javascriptasync function clickAndDebug(page, selector) { console.log(`Attempting to click: ${selector}`); try { // Check if element exists const element = await page.$(selector); if (!element) { throw new Error(`Element not found: ${selector}`); } // Check if element is visible const isVisible = await element.isIntersectingViewport(); if (!isVisible) { console.warn('Element is not visible, scrolling to it'); await element.scrollIntoView(); } await element.click(); console.log(`Successfully clicked: ${selector}`); } catch (error) { console.error(`Failed to click: ${selector}`, error); await page.screenshot({ path: 'debug-click-failed.png' }); throw error; } }
9. Common Errors and Solutions
Error 1: Element Not Found
javascript// Problem: Wrong element selector await page.click('.wrong-selector'); // Solution: Use correct selector await page.click('.correct-selector'); // Or wait for element to appear await page.waitForSelector('.correct-selector'); await page.click('.correct-selector');
Error 2: Element Not Clickable
javascript// Problem: Element is obscured or not visible await page.click('.hidden-button'); // Solution: Scroll to element await page.evaluate(selector => { document.querySelector(selector).scrollIntoView(); }, '.hidden-button'); await page.click('.hidden-button');
Error 3: Timeout Error
javascript// Problem: Page load timeout await page.goto('https://slow-website.com'); // Solution: Increase timeout await page.goto('https://slow-website.com', { timeout: 60000 }); // Or use more lenient wait condition await page.goto('https://slow-website.com', { waitUntil: 'domcontentloaded' });
Error 4: Memory Leak
javascript// Problem: Browser instance not closed const browser = await puppeteer.launch(); // Forgot to close // Solution: Use finally to ensure closure const browser = await puppeteer.launch(); try { // Operations } finally { await browser.close(); }
10. Debugging Best Practices
1. Use Descriptive Logging:
javascriptconsole.log(`[INFO] Navigating to ${url}`); console.log(`[DEBUG] Found ${elements.length} elements`); console.log(`[ERROR] Failed to click button: ${error.message}`);
2. Save Debug Information:
javascriptconst debugInfo = { url: page.url(), timestamp: new Date().toISOString(), screenshot: await page.screenshot({ encoding: 'base64' }), html: await page.content(), cookies: await page.cookies() }; require('fs').writeFileSync('debug.json', JSON.stringify(debugInfo, null, 2));
3. Use Conditional Breakpoints:
javascriptawait page.evaluate(() => { debugger; // Pause in browser });
4. Step-by-Step Debugging:
javascript// Use slowMo to slow down operations const browser = await puppeteer.launch({ slowMo: 100 }); // Or add delays at key steps await new Promise(resolve => setTimeout(resolve, 1000));
5. Use Debugger:
javascript// Add debugger in code debugger; // Run with Node.js debugger node --inspect-brk script.js
11. Testing and Validation
Unit Test Example:
javascriptconst assert = require('assert'); async function testPageLoad() { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://example.com'); const title = await page.title(); assert.strictEqual(title, 'Example Domain'); await browser.close(); } testPageLoad().catch(console.error);
Integration Test Example:
javascriptasync function testUserFlow() { const browser = await puppeteer.launch(); const page = await browser.newPage(); // Test login flow await page.goto('https://example.com/login'); await page.type('#username', 'testuser'); await page.type('#password', 'password'); await page.click('#login-button'); // Verify login success await page.waitForSelector('.user-profile'); const isLoggedIn = await page.$('.user-profile') !== null; assert(isLoggedIn, 'Login failed'); await browser.close(); } testUserFlow().catch(console.error);