From b25dfc18ecf12d6307ee66a79cc4cc7c4453dc45 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 17 Aug 2022 18:26:27 -0600 Subject: [PATCH] Add query key ordering fix --- lib/mock/mock-utils.js | 21 ++++++++++++++++-- test/mock-client.js | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/lib/mock/mock-utils.js b/lib/mock/mock-utils.js index 7e115f83b4d..c00ee08e07d 100644 --- a/lib/mock/mock-utils.js +++ b/lib/mock/mock-utils.js @@ -85,6 +85,22 @@ function matchHeaders (mockDispatch, headers) { return true } +function safeUrl (path) { + if (typeof path !== 'string') { + return path + } + + const pathSegments = path.split('?') + + if (pathSegments.length !== 2) { + return path + } + + const qp = new URLSearchParams(pathSegments.pop()) + qp.sort() + return [...pathSegments, qp.toString()].join('?') +} + function matchKey (mockDispatch, { path, method, body, headers }) { const pathMatch = matchValue(mockDispatch.path, path) const methodMatch = matchValue(mockDispatch.method, method) @@ -104,10 +120,11 @@ function getResponseData (data) { } function getMockDispatch (mockDispatches, key) { - const resolvedPath = key.query ? buildURL(key.path, key.query) : key.path + const basePath = key.query ? buildURL(key.path, key.query) : key.path + const resolvedPath = typeof basePath === 'string' ? safeUrl(basePath) : basePath // Match path - let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(path, resolvedPath)) + let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(safeUrl(path), resolvedPath)) if (matchedMockDispatches.length === 0) { throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`) } diff --git a/test/mock-client.js b/test/mock-client.js index db197ec6788..ef0600e68d2 100644 --- a/test/mock-client.js +++ b/test/mock-client.js @@ -316,6 +316,54 @@ test('MockClient - should intercept query params with hardcoded path', async (t) t.same(response, 'hello') }) +test('MockClient - should intercept query params regardless of key ordering', async (t) => { + t.plan(3) + + const server = createServer((req, res) => { + res.setHeader('content-type', 'text/plain') + res.end('should not be called') + t.fail('should not be called') + t.end() + }) + t.teardown(server.close.bind(server)) + + await promisify(server.listen.bind(server))(0) + + const baseUrl = `http://localhost:${server.address().port}` + + const mockAgent = new MockAgent({ connections: 1 }) + t.teardown(mockAgent.close.bind(mockAgent)) + + const mockClient = mockAgent.get(baseUrl) + t.type(mockClient, MockClient) + setGlobalDispatcher(mockClient) + + const query = { + pageNum: 1, + limit: 100, + ordering: [false, true] + } + + mockClient.intercept({ + path: '/foo', + query: { + ordering: query.ordering, + pageNum: query.pageNum, + limit: query.limit + }, + method: 'GET' + }).reply(200, 'hello') + + const { statusCode, body } = await request(`${baseUrl}/foo`, { + method: 'GET', + query + }) + t.equal(statusCode, 200) + + const response = await getResponse(body) + t.same(response, 'hello') +}) + test('MockClient - should be able to use as a local dispatcher', async (t) => { t.plan(3)