@luminati-io/webdriverio8
Version:
Next-gen browser and mobile automation test framework for Node.js
268 lines • 24.2 kB
JavaScript
import fs from 'node:fs/promises';
import path from 'node:path';
import logger from '@wdio/logger';
import Interception from './index.js';
import { containsHeaderObject } from '../index.js';
import { ERROR_REASON } from '../../constants.js';
import { CDP_SESSIONS, SESSION_MOCKS } from '../../commands/browser/mock.js';
const log = logger('webdriverio');
export default class DevtoolsInterception extends Interception {
restored = false;
static handleRequestInterception(client, mocks) {
return async (event) => {
// responseHeaders and responseStatusCode are only present in Response stage
// https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused
const isRequest = !event.responseHeaders && !event.responseErrorReason;
event.responseStatusCode ||= event.responseErrorReason ? 0 : 200;
event.responseHeaders ||= [];
const responseHeaders = event.responseHeaders.reduce((headers, { name, value }) => {
headers[name] = value;
return headers;
}, {});
let mockResponded = false;
for (const mock of mocks) {
/**
* skip response mocks in Request stage
*/
if (isRequest && (mock.respondOverwrites.length === 0 || // nothing to do in Request stage
(!mock.respondOverwrites[0].errorReason && // skip if not going to abort a request
// or want to fetch response
mock.respondOverwrites[0].params &&
mock.respondOverwrites[0].params.fetchResponse !== false))) {
continue;
}
/**
* match mock url
*/
if (!Interception.isMatchingRequest(mock.url, event.request.url)) {
continue;
}
/**
* Add statusCode and responseHeaders to request to be used in expect-webdriverio
*/
event.request.statusCode = event.responseStatusCode;
event.request.responseHeaders = { ...responseHeaders };
/**
* match filter options
*/
if (filterMethod(event.request.method, mock.filterOptions.method) ||
filterHeaders(event.request.headers, mock.filterOptions.requestHeaders) ||
filterHeaders(responseHeaders, mock.filterOptions.headers) ||
filterRequest(event.request.postData, mock.filterOptions.postData) ||
filterStatusCode(event.responseStatusCode, mock.filterOptions.statusCode)) {
continue;
}
mock.emit('request', event);
const { requestId, request, responseStatusCode } = event;
const { body, base64Encoded = undefined } = isRequest ? { body: '' } : await client.send('Fetch.getResponseBody', { requestId }).catch(/* istanbul ignore next */ () => ({}));
request.body = base64Encoded ? Buffer.from(body, 'base64').toString('utf8') : body;
const contentTypeHeader = Object.keys(responseHeaders).find(h => h.toLowerCase() === 'content-type') || '';
const responseContentType = responseHeaders[contentTypeHeader];
request.body = responseContentType && responseContentType.includes('application/json')
? tryParseJson(request.body)
: request.body;
mock.matches.push(request);
/**
* no stubbing if no overwrites were defined
*/
if (mockResponded || mock.respondOverwrites.length === 0) {
mock.emit('match', request);
mock.emit('continue', requestId);
continue;
}
const { errorReason, overwrite, params = {} } = mock.respondOverwrites[0].sticky
? mock.respondOverwrites[0]
: mock.respondOverwrites.shift() || {};
/**
* when response is modified
*/
if (overwrite !== undefined) {
let newBody = overwrite;
if (typeof overwrite === 'function') {
newBody = await overwrite(request, client);
}
const isBodyUndefined = typeof newBody === 'undefined';
if (isBodyUndefined) {
newBody = '';
}
if (typeof newBody !== 'string') {
newBody = JSON.stringify(newBody);
}
let responseCode = typeof params.statusCode === 'function' ? params.statusCode(request) : params.statusCode || responseStatusCode;
let responseHeaders = [
...event.responseHeaders,
...Object.entries(typeof params.headers === 'function' ? params.headers(request) : params.headers || {}).map(([name, value]) => ({ name, value }))
];
/**
* check if local file and load it
*/
const responseFilePath = path.isAbsolute(newBody) ? newBody : path.join(process.cwd(), newBody);
const responseFileAccessible = await fs.access(responseFilePath).then(() => true, () => false);
if (newBody.length > 0 && responseFileAccessible) {
newBody = (await fs.readFile(responseFilePath));
}
else if (newBody.startsWith('http')) {
responseCode = 301;
/**
* filter out possible available location header
*/
responseHeaders = responseHeaders.filter(({ name }) => name.toLowerCase() !== 'location');
responseHeaders.push({ name: 'Location', value: newBody });
}
request.mockedResponse = newBody;
mock.emit('match', request);
const overwriteData = { requestId, responseCode, responseHeaders, body: isBodyUndefined ? undefined : newBody };
mock.emit('overwrite', overwriteData);
mockResponded = true;
const body = typeof overwriteData.body === 'undefined'
? undefined
: (overwriteData.body instanceof Buffer
? overwriteData.body
: Buffer.from(overwriteData.body, 'utf8')).toString('base64');
await client.send('Fetch.fulfillRequest', {
...overwriteData,
body
}).catch(/* istanbul ignore next */ logFetchError);
continue;
}
/**
* when request is aborted
*/
if (errorReason) {
const failData = { requestId, errorReason };
mock.emit('fail', failData);
mockResponded = true;
await client.send('Fetch.failRequest', failData).catch(/* istanbul ignore next */ logFetchError);
continue;
}
}
if (!mockResponded) {
return client.send('Fetch.continueRequest', { requestId: event.requestId }).catch(/* istanbul ignore next */ logFetchError);
}
};
}
/**
* allows access to all requests made with given pattern
*/
get calls() {
return this.matches;
}
/**
* Resets all information stored in the `mock.calls` set.
*/
clear() {
this.matches = [];
}
/**
* Does everything that `mock.clear()` does, and also
* removes any mocked return values or implementations.
* Restored mock does not emit events and could not mock responses
*/
async restore(sessionMocks = SESSION_MOCKS, cdpSessions = CDP_SESSIONS) {
this.clear();
this.respondOverwrites = [];
this.restored = true;
const handle = await this.browser.getWindowHandle();
log.trace(`Restoring mock for ${handle}`);
sessionMocks[handle].delete(this);
if (sessionMocks[handle].size) {
return;
}
log.trace(`Disabling fetch domain for ${handle}`);
return cdpSessions[handle].send('Fetch.disable')
.then(() => {
delete sessionMocks[handle];
delete cdpSessions[handle];
}).catch(/* istanbul ignore next */ logFetchError);
}
/**
* Always respond with same overwrite
* @param {*} overwrites payload to overwrite the response
* @param {*} params additional respond parameters to overwrite
*/
respond(overwrite, params = {}) {
this.ensureNotRestored();
this.respondOverwrites.push({ overwrite, params, sticky: true });
}
/**
* Respond request once with given overwrite
* @param {*} overwrites payload to overwrite the response
* @param {*} params additional respond parameters to overwrite
*/
respondOnce(overwrite, params = {}) {
this.ensureNotRestored();
this.respondOverwrites.push({ overwrite, params });
}
/**
* Abort the request with an error code
* @param {string} errorCode error code of the response
*/
abort(errorReason, sticky = true) {
this.ensureNotRestored();
if (typeof errorReason !== 'string' || !ERROR_REASON.includes(errorReason)) {
throw new Error(`Invalid value for errorReason, allowed are: ${ERROR_REASON.join(', ')}`);
}
this.respondOverwrites.push({ errorReason, sticky });
}
/**
* Abort the request once with an error code
* @param {string} errorReason error code of the response
*/
abortOnce(errorReason) {
this.abort(errorReason, false);
}
ensureNotRestored() {
if (this.restored) {
throw new Error('This can\'t be done on restored mock');
}
}
}
const filterMethod = (method, expected) => {
if (typeof expected === 'undefined') {
return false;
}
if (typeof expected === 'function') {
return expected(method) !== true;
}
return expected.toLowerCase() !== method.toLowerCase();
};
const filterHeaders = (responseHeaders, expected) => {
if (typeof expected === 'undefined') {
return false;
}
if (typeof expected === 'function') {
return expected(responseHeaders) !== true;
}
return !containsHeaderObject(responseHeaders, expected);
};
const filterRequest = (postData, expected) => {
if (typeof expected === 'undefined') {
return false;
}
if (typeof expected === 'function') {
return expected(postData) !== true;
}
return postData !== expected;
};
const filterStatusCode = (statusCode, expected) => {
if (typeof expected === 'undefined') {
return false;
}
if (typeof expected === 'function') {
return expected(statusCode) !== true;
}
return statusCode !== expected;
};
const tryParseJson = (body) => {
try {
return JSON.parse(body) || body;
}
catch {
return body;
}
};
const logFetchError = (err) => {
/* istanbul ignore next */
log.debug(err?.message);
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGV2dG9vbHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvdXRpbHMvaW50ZXJjZXB0aW9uL2RldnRvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBQ2pDLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQTtBQUM1QixPQUFPLE1BQU0sTUFBTSxjQUFjLENBQUE7QUFJakMsT0FBTyxZQUFZLE1BQU0sWUFBWSxDQUFBO0FBRXJDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGFBQWEsQ0FBQTtBQUNsRCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sb0JBQW9CLENBQUE7QUFDakQsT0FBTyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQTtBQUU1RSxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUE7QUFzQmpDLE1BQU0sQ0FBQyxPQUFPLE9BQU8sb0JBQXFCLFNBQVEsWUFBWTtJQUNsRCxRQUFRLEdBQUcsS0FBSyxDQUFBO0lBRXhCLE1BQU0sQ0FBQyx5QkFBeUIsQ0FBRSxNQUFrQixFQUFFLEtBQXdCO1FBQzFFLE9BQU8sS0FBSyxFQUFFLEtBQUssRUFBRSxFQUFFO1lBQ25CLDRFQUE0RTtZQUM1RSxvRkFBb0Y7WUFDcEYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxLQUFLLENBQUMsZUFBZSxJQUFJLENBQUMsS0FBSyxDQUFDLG1CQUFtQixDQUFBO1lBRXRFLEtBQUssQ0FBQyxrQkFBa0IsS0FBSyxLQUFLLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFBO1lBQ2hFLEtBQUssQ0FBQyxlQUFlLEtBQUssRUFBRSxDQUFBO1lBRTVCLE1BQU0sZUFBZSxHQUFHLEtBQUssQ0FBQyxlQUFlLENBQUMsTUFBTSxDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLEtB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