Skip to content

Commit e46d8bf

Browse files
authored
Add query key ordering fix (#1607)
1 parent d83c1e2 commit e46d8bf

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

lib/mock/mock-utils.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,22 @@ function matchHeaders (mockDispatch, headers) {
8585
return true
8686
}
8787

88+
function safeUrl (path) {
89+
if (typeof path !== 'string') {
90+
return path
91+
}
92+
93+
const pathSegments = path.split('?')
94+
95+
if (pathSegments.length !== 2) {
96+
return path
97+
}
98+
99+
const qp = new URLSearchParams(pathSegments.pop())
100+
qp.sort()
101+
return [...pathSegments, qp.toString()].join('?')
102+
}
103+
88104
function matchKey (mockDispatch, { path, method, body, headers }) {
89105
const pathMatch = matchValue(mockDispatch.path, path)
90106
const methodMatch = matchValue(mockDispatch.method, method)
@@ -104,10 +120,11 @@ function getResponseData (data) {
104120
}
105121

106122
function getMockDispatch (mockDispatches, key) {
107-
const resolvedPath = key.query ? buildURL(key.path, key.query) : key.path
123+
const basePath = key.query ? buildURL(key.path, key.query) : key.path
124+
const resolvedPath = typeof basePath === 'string' ? safeUrl(basePath) : basePath
108125

109126
// Match path
110-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(path, resolvedPath))
127+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(safeUrl(path), resolvedPath))
111128
if (matchedMockDispatches.length === 0) {
112129
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`)
113130
}

test/mock-client.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,54 @@ test('MockClient - should intercept query params with hardcoded path', async (t)
316316
t.same(response, 'hello')
317317
})
318318

319+
test('MockClient - should intercept query params regardless of key ordering', async (t) => {
320+
t.plan(3)
321+
322+
const server = createServer((req, res) => {
323+
res.setHeader('content-type', 'text/plain')
324+
res.end('should not be called')
325+
t.fail('should not be called')
326+
t.end()
327+
})
328+
t.teardown(server.close.bind(server))
329+
330+
await promisify(server.listen.bind(server))(0)
331+
332+
const baseUrl = `http://localhost:${server.address().port}`
333+
334+
const mockAgent = new MockAgent({ connections: 1 })
335+
t.teardown(mockAgent.close.bind(mockAgent))
336+
337+
const mockClient = mockAgent.get(baseUrl)
338+
t.type(mockClient, MockClient)
339+
setGlobalDispatcher(mockClient)
340+
341+
const query = {
342+
pageNum: 1,
343+
limit: 100,
344+
ordering: [false, true]
345+
}
346+
347+
mockClient.intercept({
348+
path: '/foo',
349+
query: {
350+
ordering: query.ordering,
351+
pageNum: query.pageNum,
352+
limit: query.limit
353+
},
354+
method: 'GET'
355+
}).reply(200, 'hello')
356+
357+
const { statusCode, body } = await request(`${baseUrl}/foo`, {
358+
method: 'GET',
359+
query
360+
})
361+
t.equal(statusCode, 200)
362+
363+
const response = await getResponse(body)
364+
t.same(response, 'hello')
365+
})
366+
319367
test('MockClient - should be able to use as a local dispatcher', async (t) => {
320368
t.plan(3)
321369

0 commit comments

Comments
 (0)