UNPKG

@applitools/eyes-storybook

Version:
112 lines (105 loc) 4.52 kB
/* eslint-disable no-restricted-globals */ const {setTimeout} = require('timers/promises'); const utils = require('@applitools/utils'); const cachedFetch = utils.general.cachify(fetchRequest); function makeBrowserNetworkPolicy({page, logger, pageId = null}) { const scope = pageId ? `[page ${pageId}]` : '[master page]'; return { extend(options) { return makeBrowserNetworkPolicy({page, logger, pageId, ...options}); }, async startInterception({timeout, blockPatterns, browserHeadersOverride, cache}) { const fetch = cache ? cachedFetch : fetchRequest; if (timeout || blockPatterns || browserHeadersOverride || cache) { const cdp = await page.target().createCDPSession(); await cdp.send('Fetch.enable'); await cdp.on('Fetch.requestPaused', async ({requestId, request, ...others}) => { logger.log(`${scope} Request to ${request.url}`); try { if (blockPatterns && blockPatterns.some(pattern => request.url.includes(pattern))) { logger.log(`${scope} Blocking request to ${request.url}`); logger.log(others); try { await cdp.send('Fetch.failRequest', {requestId, errorReason: 'BlockedByClient'}); } catch (err) { logger.log(`${scope} Failed to block request to ${request.url}. Error: ${err}`); } return; } if (browserHeadersOverride) { logger.log(`${scope} Overriding headers for request to ${request.url}`); request.headers = applyHeaderOverrides(request.headers, browserHeadersOverride); } if (timeout) { return await Promise.race([ setTimeout(timeout, {action: 'abort'}), fetch(request) .then(response => ({action: 'fulfill', response})) .catch(() => ({action: 'fail'})), ]).then(async ({action, response}) => { if (action === 'abort') { logger.log(`${scope} Timeout for request to ${request.url}`); await cdp.send('Fetch.failRequest', {requestId, errorReason: 'Aborted'}); } else if (action === 'fulfill') { logger.log(`${scope} Fulfilled request to ${request.url}`); await cdp.send('Fetch.fulfillRequest', {...response, requestId}); } else { logger.log(`${scope} Failed request to ${request.url}`); await cdp.send('Fetch.failRequest', {requestId, errorReason: 'Failed'}); } return; }); } if (browserHeadersOverride || cache) { logger.log(`${scope} Fetching request to ${request.url}`); const response = await fetch(request); logger.log(`${scope} Fulfilled request to ${request.url}`); await cdp.send('Fetch.fulfillRequest', {...response, requestId}); return; } else { logger.log(`${scope} Not intercepting request to ${request.url}`); await cdp.send('Fetch.continueRequest', { requestId, }); } } catch (err) { // I beliebe it doens't necessarily indicate an error - if we get here, the request was cancled (e.g. by navigating away). Logging it for now. logger.log(`${scope} Failed to intercept request to ${request.url}. Error: ${err}`); } }); } }, }; } async function fetchRequest(request) { const response = await fetch(request.url, { method: request.method, headers: request.headers, body: request.postData, }); return { responseCode: response.status, responseHeaders: [...response.headers.entries()].map(([name, value]) => ({ name, value, })), body: Buffer.from(await response.arrayBuffer()).toString('base64'), }; } function applyHeaderOverrides(headers, overrides) { const result = {...headers}; for (const [name, value] of Object.entries(overrides)) { if (typeof value === 'function') { result[name] = value(result[name], headers); } else if (value === null || value === undefined) { result[name] = null; } else { result[name] = value; } } return result; } module.exports = { makeNetworkUtils: makeBrowserNetworkPolicy, clearNetworkCache: cachedFetch.clearCache, // for testing purposes };