[READ-ONLY] a fast, modern browser for the npm registry
at main 90 lines 3.3 kB view raw
1/** 2 * Lighthouse CI puppeteer setup script. 3 * 4 * Sets the color mode (light/dark) before running accessibility audits 5 * and intercepts client-side API requests using the same fixture data 6 * as the Playwright E2E tests. 7 * 8 * The color mode is determined by the LIGHTHOUSE_COLOR_MODE environment variable. 9 * If not set, defaults to 'dark'. 10 * 11 * Request interception uses CDP (Chrome DevTools Protocol) Fetch domain 12 * at the browser level, which avoids conflicts with Lighthouse's own 13 * Puppeteer-level request interception. 14 */ 15 16const mockRoutes = require('./test/fixtures/mock-routes.cjs') 17 18module.exports = async function setup(browser, { url }) { 19 const colorMode = process.env.LIGHTHOUSE_COLOR_MODE || 'dark' 20 21 // Set up browser-level request interception via CDP Fetch domain. 22 // This operates below Puppeteer's request interception layer so it 23 // doesn't conflict with Lighthouse's own setRequestInterception usage. 24 await setupCdpRequestInterception(browser) 25 26 const page = await browser.newPage() 27 28 // Set localStorage before navigating so @nuxtjs/color-mode picks it up 29 await page.evaluateOnNewDocument(mode => { 30 localStorage.setItem('npmx-color-mode', mode) 31 }, colorMode) 32 33 // Navigate and wait for DOM only - Lighthouse will do its own full load 34 await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 }) 35 36 // Close the page - Lighthouse will open its own with localStorage already set 37 await page.close() 38} 39 40/** 41 * Set up request interception using CDP's Fetch domain on the browser's 42 * default context. This intercepts requests at a lower level than Puppeteer's 43 * page.setRequestInterception(), avoiding "Request is already handled!" errors 44 * when Lighthouse sets up its own interception. 45 * 46 * @param {import('puppeteer').Browser} browser 47 */ 48async function setupCdpRequestInterception(browser) { 49 // Build URL pattern list for CDP Fetch.enable from our route definitions 50 const cdpPatterns = mockRoutes.routes.map(route => ({ 51 urlPattern: route.pattern.replace('/**', '/*'), 52 requestStage: 'Request', 53 })) 54 55 // Listen for new targets so we can attach CDP interception to each page 56 browser.on('targetcreated', async target => { 57 if (target.type() !== 'page') return 58 59 try { 60 const cdp = await target.createCDPSession() 61 62 cdp.on('Fetch.requestPaused', async event => { 63 const requestUrl = event.request.url 64 const result = mockRoutes.matchRoute(requestUrl) 65 66 if (result) { 67 const body = Buffer.from(result.response.body).toString('base64') 68 await cdp.send('Fetch.fulfillRequest', { 69 requestId: event.requestId, 70 responseCode: result.response.status, 71 responseHeaders: [ 72 { name: 'Content-Type', value: result.response.contentType }, 73 { name: 'Access-Control-Allow-Origin', value: '*' }, 74 ], 75 body, 76 }) 77 } else { 78 await cdp.send('Fetch.continueRequest', { 79 requestId: event.requestId, 80 }) 81 } 82 }) 83 84 await cdp.send('Fetch.enable', { patterns: cdpPatterns }) 85 } catch { 86 // Target may have been closed before we could attach. 87 // This is expected for transient targets like service workers. 88 } 89 }) 90}