UNPKG

@luminati-io/webdriverio8

Version:

Next-gen browser and mobile automation test framework for Node.js

268 lines 24.2 kB
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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