Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(MockInterceptor): callback options.headers w/ fetch #1559

Merged
merged 1 commit into from Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 18 additions & 7 deletions lib/mock/mock-utils.js
Expand Up @@ -51,15 +51,20 @@ function getHeaderByName (headers, key) {
}
}

/** @param {string[]} headers */
function buildHeadersFromArray (headers) { // fetch HeadersList
const clone = headers.slice()
const entries = []
for (let index = 0; index < clone.length; index += 2) {
entries.push([clone[index], clone[index + 1]])
}
return Object.fromEntries(entries)
}

function matchHeaders (mockDispatch, headers) {
if (typeof mockDispatch.headers === 'function') {
if (Array.isArray(headers)) { // fetch HeadersList
const clone = headers.slice()
const entries = []
for (let index = 0; index < clone.length; index += 2) {
entries.push([clone[index], clone[index + 1]])
}
headers = Object.fromEntries(entries)
headers = buildHeadersFromArray(headers)
}
return mockDispatch.headers(headers ? lowerCaseEntries(headers) : {})
}
Expand Down Expand Up @@ -284,7 +289,13 @@ function mockDispatch (opts, handler) {
}

function handleReply (mockDispatches) {
const responseData = getResponseData(typeof data === 'function' ? data(opts) : data)
// fetch's HeadersList is a 1D string array
const optsHeaders = Array.isArray(opts.headers)
? buildHeadersFromArray(opts.headers)
: opts.headers
const responseData = getResponseData(
typeof data === 'function' ? data({ ...opts, headers: optsHeaders }) : data
)
const responseHeaders = generateKeyValues(headers)
const responseTrailers = generateKeyValues(trailers)

Expand Down
32 changes: 32 additions & 0 deletions test/mock-agent.js
Expand Up @@ -2431,3 +2431,35 @@ test('MockAgent - using fetch yields correct statusText', { skip: nodeMajor < 16

t.end()
})

// https://github.com/nodejs/undici/issues/1556
test('MockAgent - using fetch yields a headers object in the reply callback', { skip: nodeMajor < 16 }, async (t) => {
const { fetch } = require('..')

const mockAgent = new MockAgent()
mockAgent.disableNetConnect()
t.teardown(mockAgent.close.bind(mockAgent))

const mockPool = mockAgent.get('http://localhost:3000')

mockPool.intercept({
path: '/headers',
method: 'GET'
}).reply(200, (opts) => {
t.same(opts.headers, {
accept: '*/*',
'accept-language': '*',
'sec-fetch-mode': 'cors',
'user-agent': 'undici',
'accept-encoding': 'gzip, deflate'
})

return {}
})

await fetch('http://localhost:3000/headers', {
dispatcher: mockAgent
})

t.end()
})
4 changes: 4 additions & 0 deletions test/types/mock-interceptor.test-d.ts
Expand Up @@ -36,6 +36,10 @@ expectAssignable<BodyInit | Dispatcher.DispatchOptions['body']>(mockResponseCall
expectAssignable<MockScope>(mockInterceptor.reply(() => ({ statusCode: 200, data: { foo: 'bar' }, responseOptions: {
trailers: { foo: 'bar' }
}})))
mockInterceptor.reply((options) => {
expectAssignable<MockInterceptor.MockResponseCallbackOptions['headers']>(options.headers);
return { statusCode: 200, data: { foo: 'bar' } }
})

// replyWithError
class CustomError extends Error {
Expand Down
2 changes: 1 addition & 1 deletion types/mock-interceptor.d.ts
Expand Up @@ -74,7 +74,7 @@ declare namespace MockInterceptor {
origin: string;
method: string;
body?: BodyInit | Dispatcher.DispatchOptions['body'];
headers: Headers;
headers: Headers | Record<string, string>;
maxRedirections: number;
}

Expand Down