diff --git a/docs/docs/@fetch-mock/core/CallHistory.md b/docs/docs/@fetch-mock/core/CallHistory.md index 8954a3bc..e9d1f414 100644 --- a/docs/docs/@fetch-mock/core/CallHistory.md +++ b/docs/docs/@fetch-mock/core/CallHistory.md @@ -10,14 +10,15 @@ sidebar_position: 4 Calls are recorded, and returned, in a standard format with the following properties: -- `[string|Request,Object]` - the original arguments passed in to `fetch` -- `{string} url` - The url being fetched -- `{NormalizedRequestOptions} options` - The options passed in to the fetch (may be derived from a `Request` if one was used) -- `{Request} [request]` - The `Request` passed to fetch, if one was used -- `{Route} [route]` - The route used to handle the request -- `{Response} [response]` - The `Response` returned to the user -- `{Object.}` - Any express parameters extracted from the `url` -- `{Promise[]} pendingPromises` - An internal structure used by the `.flush()` method documented below +- **arguments** `[string|Request,Object]` - the original arguments passed in to `fetch` +- **url** `{string}` - The url being fetched +- **options** `{NormalizedRequestOptions}` - The options passed in to the fetch (may be derived from a `Request` if one was used) +- **request** `{Request}` - The `Request` passed to fetch, if one was used +- **route** `{Route}` - The route used to handle the request +- **response** `{Response}` - The `Response` returned to the user +- **expressParams** `{Object.}` - Any express parameters extracted from the `url` +- **queryParams** `{URLSearchParams}` - Any query parameters extracted from the `url` +- **pendingPromises** `{Promise[]} ` - An internal structure used by the `.flush()` method documented below ## Filtering diff --git a/package-lock.json b/package-lock.json index 42c59e19..84f6ef3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21933,15 +21933,6 @@ "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", "dev": true }, - "node_modules/querystring": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", - "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -27172,13 +27163,12 @@ }, "packages/core": { "name": "@fetch-mock/core", - "version": "0.4.0", + "version": "0.4.1", "license": "ISC", "dependencies": { "dequal": "^2.0.3", "globrex": "^0.1.2", "is-subset-of": "^3.1.10", - "querystring": "^0.2.1", "regexparam": "^3.0.0" } }, diff --git a/package.json b/package.json index 026e386e..73a964b7 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "lint:ci": "eslint --ext .js,.cjs .", "prettier": "prettier --cache --write *.md \"./**/*.md\"", "prettier:ci": "prettier *.md \"./**/*.md\"", - "types:check": "tsc --project ./jsconfig.json", + "types:check": "tsc --project ./jsconfig.json && echo 'types check done'", "types:lint": "dtslint --expectOnly packages/fetch-mock/types", "prepare": "husky || echo \"husky not available\"", "build": "npm run build -w=packages/fetch-mock -w=packages/core", diff --git a/packages/core/package.json b/packages/core/package.json index a249f81e..b759cd80 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,7 +28,6 @@ "dequal": "^2.0.3", "globrex": "^0.1.2", "is-subset-of": "^3.1.10", - "querystring": "^0.2.1", "regexparam": "^3.0.0" } } diff --git a/packages/core/src/CallHistory.js b/packages/core/src/CallHistory.js index 5173704d..c5136def 100644 --- a/packages/core/src/CallHistory.js +++ b/packages/core/src/CallHistory.js @@ -19,7 +19,7 @@ import Router from './Router.js'; * @property {Route} [route] * @property {Response} [response] * @property {Object.} [expressParams] - * @property {Object.} [queryParams] + * @property {URLSearchParams} [queryParams] * @property {Promise[]} pendingPromises */ diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.js index 14a8162f..665a7bdd 100644 --- a/packages/core/src/Matchers.js +++ b/packages/core/src/Matchers.js @@ -3,15 +3,9 @@ /** @typedef {import('./CallHistory.js').CallLog} CallLog */ import glob from 'globrex'; import * as regexparam from 'regexparam'; -import querystring from 'querystring'; import { isSubsetOf } from 'is-subset-of'; import { dequal as isEqual } from 'dequal'; -import { - normalizeHeaders, - getPath, - getQuery, - normalizeUrl, -} from './RequestUtils.js'; +import { normalizeHeaders, getPath, normalizeUrl } from './RequestUtils.js'; /** @typedef {string | RegExp | URL} RouteMatcherUrl */ /** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */ @@ -113,32 +107,55 @@ const getMethodMatcher = ({ method: expectedMethod }) => { /** * @type {MatcherGenerator} */ -const getQueryStringMatcher = ({ query: passedQuery }) => { +const getQueryParamsMatcher = ({ query: passedQuery }) => { if (!passedQuery) { return; } - const expectedQuery = querystring.parse(querystring.stringify(passedQuery)); - const keys = Object.keys(expectedQuery); - return ({ url }) => { - const query = querystring.parse(getQuery(url)); + const expectedQuery = new URLSearchParams(); + for (const [key, value] of Object.entries(passedQuery)) { + if (Array.isArray(value)) { + for (const item of value) { + expectedQuery.append( + key, + typeof item === 'object' || typeof item === 'undefined' + ? '' + : item.toString(), + ); + } + } else { + expectedQuery.append( + key, + typeof value === 'object' || typeof value === 'undefined' + ? '' + : value.toString(), + ); + } + } + + const keys = Array.from(expectedQuery.keys()); + return ({ queryParams }) => { return keys.every((key) => { - if (Array.isArray(query[key])) { - if (!Array.isArray(expectedQuery[key])) { - return false; - } - return isEqual( - /** @type {string[]}*/ (query[key]).sort(), - /** @type {string[]}*/ (expectedQuery[key]).sort(), + const expectedValues = expectedQuery.getAll(key).sort(); + const actualValues = queryParams.getAll(key).sort(); + + if (expectedValues.length !== actualValues.length) { + return false; + } + + if (Array.isArray(passedQuery[key])) { + return expectedValues.every( + (expected, index) => expected === actualValues[index], ); } - return query[key] === expectedQuery[key]; + + return isEqual(actualValues, expectedValues); }); }; }; /** * @type {MatcherGenerator} */ -const getParamsMatcher = ({ params: expectedParams, url }) => { +const getExpressParamsMatcher = ({ params: expectedParams, url }) => { if (!expectedParams) { return; } @@ -248,10 +265,10 @@ const getUrlMatcher = (route) => { /** @type {MatcherDefinition[]} */ export const builtInMatchers = [ { name: 'url', matcher: getUrlMatcher }, - { name: 'query', matcher: getQueryStringMatcher }, + { name: 'query', matcher: getQueryParamsMatcher }, { name: 'method', matcher: getMethodMatcher }, { name: 'headers', matcher: getHeaderMatcher }, - { name: 'params', matcher: getParamsMatcher }, + { name: 'params', matcher: getExpressParamsMatcher }, { name: 'body', matcher: getBodyMatcher, usesBody: true }, { name: 'matcherFunction', matcher: getFunctionMatcher }, ]; diff --git a/packages/core/src/RequestUtils.js b/packages/core/src/RequestUtils.js index a1f36744..e4a5b947 100644 --- a/packages/core/src/RequestUtils.js +++ b/packages/core/src/RequestUtils.js @@ -42,10 +42,12 @@ export function createCallLogFromUrlAndOptions(url, options) { /** @type {Promise[]} */ const pendingPromises = []; if (typeof url === 'string' || url instanceof String || url instanceof URL) { + // @ts-ignore - jsdoc doesn't distinguish between string and String, but typechecker complains + url = normalizeUrl(url); return { arguments: [url, options], - // @ts-ignore - jsdoc doesn't distinguish between string and String, but typechecker complains - url: normalizeUrl(url), + url, + queryParams: new URLSearchParams(getQuery(url)), options: options || {}, signal: options && options.signal, pendingPromises, @@ -81,9 +83,11 @@ export async function createCallLogFromRequest(request, options) { if (request.headers) { derivedOptions.headers = normalizeHeaders(request.headers); } + const url = normalizeUrl(request.url); const callLog = { arguments: [request, options], - url: normalizeUrl(request.url), + url, + queryParams: new URLSearchParams(getQuery(url)), options: Object.assign(derivedOptions, options || {}), request: request, signal: (options && options.signal) || request.signal, diff --git a/packages/core/src/__tests__/Matchers/query-string.test.js b/packages/core/src/__tests__/Matchers/query-string.test.js index 468c144d..35877710 100644 --- a/packages/core/src/__tests__/Matchers/query-string.test.js +++ b/packages/core/src/__tests__/Matchers/query-string.test.js @@ -8,19 +8,18 @@ describe('query string matching', () => { response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b&c=d' })).toBe(true); - }); - - it('match a query string against a URL object', () => { - const route = new Route({ - query: { a: 'b', c: 'd' }, - response: 200, - }); - const url = new URL('http://a.com/path'); - url.searchParams.append('a', 'b'); - url.searchParams.append('c', 'd'); - expect(route.matcher({ url })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&c=d'), + }), + ).toBe(true); }); it('match a query string against a relative path', () => { @@ -28,8 +27,12 @@ describe('query string matching', () => { query: { a: 'b' }, response: 200, }); - const url = '/path?a=b'; - expect(route.matcher({ url })).toBe(true); + expect( + route.matcher({ + url: '/path', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(true); }); it('match multiple query strings', () => { @@ -38,10 +41,30 @@ describe('query string matching', () => { response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b&c=d' })).toBe(true); - expect(route.matcher({ url: 'http://a.com?c=d&a=b' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&c=d'), + }), + ).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('c=d&a=b'), + }), + ).toBe(true); }); it('ignore irrelevant query strings', () => { @@ -50,7 +73,12 @@ describe('query string matching', () => { response: 200, }); - expect(route.matcher({ url: 'http://a.com?a=b&c=d&e=f' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&c=d&e=f'), + }), + ).toBe(true); }); it('match an empty query string', () => { const route = new Route({ @@ -58,8 +86,18 @@ describe('query string matching', () => { response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a='), + }), + ).toBe(true); }); describe('value coercion', () => { @@ -70,8 +108,18 @@ describe('query string matching', () => { }, response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=1' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=1'), + }), + ).toBe(true); }); it('coerce floats to strings and match', () => { @@ -81,8 +129,18 @@ describe('query string matching', () => { }, response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=1.2' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=1.2'), + }), + ).toBe(true); }); it('coerce booleans to strings and match', () => { @@ -99,10 +157,30 @@ describe('query string matching', () => { response: 200, }); - expect(trueRoute.matcher({ url: 'http://a.com' })).toBe(false); - expect(falseRoute.matcher({ url: 'http://a.com' })).toBe(false); - expect(trueRoute.matcher({ url: 'http://a.com?a=true' })).toBe(true); - expect(falseRoute.matcher({ url: 'http://a.com?b=false' })).toBe(true); + expect( + trueRoute.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + falseRoute.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + trueRoute.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=true'), + }), + ).toBe(true); + expect( + falseRoute.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('b=false'), + }), + ).toBe(true); }); it('coerce undefined to an empty string and match', () => { @@ -112,8 +190,18 @@ describe('query string matching', () => { }, response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a='), + }), + ).toBe(true); }); it('coerce null to an empty string and match', () => { @@ -123,8 +211,18 @@ describe('query string matching', () => { }, response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a='), + }), + ).toBe(true); }); it('coerce an object to an empty string and match', () => { @@ -134,8 +232,18 @@ describe('query string matching', () => { }, response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a='), + }), + ).toBe(true); }); it('can match a query string with different value types', () => { @@ -150,10 +258,18 @@ describe('query string matching', () => { }, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); expect( route.matcher({ - url: 'http://a.com?t=true&f=false&u=&num=1&arr=a&arr=', + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams( + 't=true&f=false&u=&num=1&arr=a&arr=', + ), }), ).toBe(true); }); @@ -163,26 +279,76 @@ describe('query string matching', () => { it('match repeated query strings', () => { const route = new Route({ query: { a: ['b', 'c'] }, response: 200 }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b&a=c' })).toBe(true); - expect(route.matcher({ url: 'http://a.com?a=b&a=c&a=d' })).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&a=c'), + }), + ).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&a=c&a=d'), + }), + ).toBe(false); }); it('match repeated query strings in any order', () => { const route = new Route({ query: { a: ['b', 'c'] }, response: 200 }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b&a=c' })).toBe(true); - expect(route.matcher({ url: 'http://a.com?a=c&a=b' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&a=c'), + }), + ).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=c&a=b'), + }), + ).toBe(true); }); it('match a query string array of length 1', () => { const route = new Route({ query: { a: ['b'] }, response: 200 }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(true); - expect(route.matcher({ url: 'http://a.com?a=b&a=c' })).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&a=c'), + }), + ).toBe(false); }); it('match a repeated query string with an empty value', () => { @@ -191,9 +357,24 @@ describe('query string matching', () => { response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b&a=' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&a='), + }), + ).toBe(true); }); }); @@ -201,15 +382,36 @@ describe('query string matching', () => { // TODO - this should probably throw when creating the route... or should it? it.skip('can be used alongside query strings expressed in the url', () => { const route = new Route({ - url: 'http://a.com/?c=d', + url: 'http://a.com', + queryParams: new URLSearchParams('/?c=d'), response: 200, query: { a: 'b' }, }); - expect(route.matcher({ url: 'http://a.com?c=d' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?c=d&a=b' })).toBe(true); - expect(route.matcher({ url: 'http://a.com?a=b&c=d' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('c=d'), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('c=d&a=b'), + }), + ).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b&c=d'), + }), + ).toBe(true); }); it('can be used alongside function matchers', () => { @@ -219,8 +421,18 @@ describe('query string matching', () => { query: { a: 'b' }, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(true); }); }); }); diff --git a/packages/core/src/__tests__/Matchers/route-config-object.test.js b/packages/core/src/__tests__/Matchers/route-config-object.test.js index e6d95cd1..4d461fc5 100644 --- a/packages/core/src/__tests__/Matchers/route-config-object.test.js +++ b/packages/core/src/__tests__/Matchers/route-config-object.test.js @@ -80,8 +80,18 @@ describe('matcher object', () => { response: 200, }); - expect(route.matcher({ url: 'http://a.com' })).toBe(false); - expect(route.matcher({ url: 'http://a.com?a=b' })).toBe(true); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams(''), + }), + ).toBe(false); + expect( + route.matcher({ + url: 'http://a.com', + queryParams: new URLSearchParams('a=b'), + }), + ).toBe(true); }); it('can match path parameter', () => { diff --git a/packages/core/src/__tests__/router-integration.test.js b/packages/core/src/__tests__/router-integration.test.js index b892e874..3b4a63e6 100644 --- a/packages/core/src/__tests__/router-integration.test.js +++ b/packages/core/src/__tests__/router-integration.test.js @@ -138,4 +138,47 @@ describe('Router', () => { ).rejects.toThrow(); }); }); + + describe('making query strings available', () => { + it('makes query string values available to matchers', async () => { + const fm = fetchMock.createInstance(); + fm.route( + { query: { a: ['a-val1', 'a-val2'], b: 'b-val', c: undefined } }, + 200, + ); + const response = await fm.fetchHandler( + 'http://a.com?a=a-val1&a=a-val2&b=b-val&c=', + ); + expect(response.status).toEqual(200); + }); + + it('always writes query string values to the callLog when using a URL', async () => { + const fm = fetchMock.createInstance(); + fm.route( + { query: { a: ['a-val1', 'a-val2'], b: 'b-val', c: undefined } }, + 200, + ); + const url = new URL('http://a.com/'); + url.searchParams.append('a', 'a-val1'); + url.searchParams.append('a', 'a-val2'); + url.searchParams.append('b', 'b-val'); + url.searchParams.append('c', undefined); + const response = await fm.fetchHandler( + 'http://a.com?a=a-val1&a=a-val2&b=b-val&c=', + ); + expect(response.status).toEqual(200); + }); + + it('always writes query string values to the callLog when using a Request', async () => { + const fm = fetchMock.createInstance(); + fm.route( + { query: { a: ['a-val1', 'a-val2'], b: 'b-val', c: undefined } }, + 200, + ); + const response = await fm.fetchHandler( + new Request('http://a.com?a=a-val1&a=a-val2&b=b-val&c='), + ); + expect(response.status).toEqual(200); + }); + }); }); diff --git a/packages/core/types/CallHistory.d.ts b/packages/core/types/CallHistory.d.ts index 1c319564..f50b9a36 100644 --- a/packages/core/types/CallHistory.d.ts +++ b/packages/core/types/CallHistory.d.ts @@ -15,9 +15,7 @@ export type CallLog = { expressParams?: { [x: string]: string; }; - queryParams?: { - [x: string]: string; - }; + queryParams?: URLSearchParams; pendingPromises: Promise[]; }; export type Matched = "matched";