@applitools/eyes-storybook
Version:
112 lines (105 loc) • 4.52 kB
JavaScript
/* 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
};