UNPKG

expect-webdriverio

Version:

WebdriverIO Assertion Library

186 lines (185 loc) 6.42 kB
import { waitUntil, enhanceError } from '../../utils.js'; import { equals } from '../../jasmineUtils.js'; import { DEFAULT_OPTIONS } from '../../constants.js'; const STR_LIMIT = 80; const KEY_LIMIT = 12; function reduceHeaders(headers) { return Object.entries(headers).reduce((acc, [, value]) => { acc[value.name] = value.value.value; return acc; }, {}); } export async function toBeRequestedWith(received, expectedValue = {}, options = DEFAULT_OPTIONS) { const isNot = this.isNot || false; const { expectation = 'called with', verb = 'be' } = this; await options.beforeAssertion?.({ matcherName: 'toBeRequestedWith', expectedValue, options, }); let actual; const pass = await waitUntil(async () => { for (const call of received.calls) { actual = call; if (methodMatcher(call.request.method, expectedValue.method) && statusCodeMatcher(call.response.status, expectedValue.statusCode) && urlMatcher(call.request.url, expectedValue.url) && headersMatcher(reduceHeaders(call.request.headers), expectedValue.requestHeaders) && headersMatcher(reduceHeaders(call.response.headers), expectedValue.responseHeaders)) { return true; } } return false; }, isNot, { ...options, wait: isNot ? 0 : options.wait }); const message = enhanceError('mock', minifyRequestedWith(expectedValue), minifyRequestMock(actual, expectedValue) || 'was not called', this, verb, expectation, '', options); const result = { pass, message: () => message }; await options.afterAssertion?.({ matcherName: 'toBeRequestedWith', expectedValue, options, result }); return result; } const methodMatcher = (method, expected) => { if (typeof expected === 'undefined') { return true; } if (!Array.isArray(expected)) { expected = [expected]; } return expected .map((m) => { if (typeof m !== 'string') { return console.error('expect.toBeRequestedWith: unsupported value passed to method ' + m); } return m.toUpperCase(); }) .includes(method); }; const statusCodeMatcher = (statusCode, expected) => { if (typeof expected === 'undefined') { return true; } if (!Array.isArray(expected)) { expected = [expected]; } return expected.includes(statusCode); }; const urlMatcher = (url, expected) => { if (typeof expected === 'undefined') { return true; } if (typeof expected === 'function') { return expected(url); } return equals(url, expected); }; const headersMatcher = (headers, expected) => { if (typeof expected === 'undefined' || typeof expected === 'object' && Object.keys(expected).length === 0) { return true; } if (typeof expected === 'function') { return expected(headers); } return equals(headers, expected); }; const isMatcher = (filter) => { return (typeof filter === 'object' && filter !== null && '__proto__' in filter && typeof filter.__proto__ === 'object' && filter.__proto__ && 'asymmetricMatch' in filter.__proto__ && typeof filter.__proto__.asymmetricMatch === 'function'); }; const minifyRequestMock = (requestMock, requestedWith) => { if (typeof requestMock === 'undefined') { return requestMock; } const r = { url: requestMock.request.url, method: requestMock.request.method, requestHeaders: requestMock.request.headers, responseHeaders: requestMock.response.headers, }; deleteUndefinedValues(r, requestedWith); return minifyRequestedWith(r); }; const minifyRequestedWith = (r) => { const result = { url: requestedWithParamToString(r.url), method: r.method, requestHeaders: requestedWithParamToString(r.requestHeaders, shortenJson), responseHeaders: requestedWithParamToString(r.responseHeaders, shortenJson), postData: requestedWithParamToString(r.postData, shortenJson), response: requestedWithParamToString(r.response, shortenJson), }; deleteUndefinedValues(result); return result; }; const requestedWithParamToString = (param, transformFn) => { if (typeof param === 'undefined') { return; } if (typeof param === 'function') { param = param.toString(); } else if (isMatcher(param)) { return (param.constructor.name + ' ' + (JSON.stringify(param.sample) || '')); } else if (transformFn && typeof param === 'object' && param !== null) { param = transformFn(param); } if (typeof param === 'string') { param = shortenString(param); } return param; }; const shortenJson = (obj, lengthLimit = STR_LIMIT * 2, keyLimit = KEY_LIMIT) => { if (JSON.stringify(obj).length < lengthLimit) { return obj; } if (Array.isArray(obj)) { const firstItem = typeof obj[0] === 'object' && obj[0] !== null ? shortenJson(obj[0], lengthLimit / 2, keyLimit / 4) : shortenString(JSON.stringify(obj[0])); return [firstItem, `... ${obj.length - 1} more items`]; } const minifiedObject = {}; const entries = Object.entries(obj); if (keyLimit >= 4) { entries.slice(0, keyLimit).forEach(([k, v]) => { if (typeof v === 'object' && v !== null) { v = shortenJson(v, lengthLimit / 2, keyLimit / 4); } else if (typeof v === 'string') { v = shortenString(v, 16); } minifiedObject[shortenString(k, 24)] = v; }); } if (entries.length > keyLimit) { minifiedObject['...'] = `${entries.length} items in total`; } return minifiedObject; }; const shortenString = (str, limit = STR_LIMIT) => { return str.length > limit ? str.substring(0, limit / 2 - 1) + '..' + str.substr(1 - limit / 2) : str; }; const deleteUndefinedValues = (obj, baseline = obj) => { Object.keys(obj).forEach((k) => { if (typeof baseline[k] === 'undefined') { delete obj[k]; } }); }; export function toBeRequestedWithResponse(...args) { return toBeRequestedWith.call(this, ...args); }