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

Use @mswjs/interceptors for mocking - WIP #2517

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
60 changes: 60 additions & 0 deletions lib/create_response.js
@@ -0,0 +1,60 @@
const { IncomingHttpHeaders, IncomingMessage } = require('http')

Check failure on line 1 in lib/create_response.js

View workflow job for this annotation

GitHub Actions / Lint JavaScript

Use the global form of 'use strict'

Check failure on line 1 in lib/create_response.js

View workflow job for this annotation

GitHub Actions / Lint JavaScript

'IncomingHttpHeaders' is assigned a value but never used

Check failure on line 1 in lib/create_response.js

View workflow job for this annotation

GitHub Actions / Lint JavaScript

'IncomingMessage' is assigned a value but never used

/**
* Creates a Fetch API `Response` instance from the given
* `http.IncomingMessage` instance.
* copied from: https://github.com/mswjs/interceptors/blob/04152ed914f8041272b6e92ed374216b8177e1b2/src/interceptors/ClientRequest/utils/createResponse.ts#L8
*/

/**
* @param {IncomingMessage} message
*/
function createResponse(message) {
const readable = new ReadableStream({

Check failure on line 13 in lib/create_response.js

View workflow job for this annotation

GitHub Actions / Lint JavaScript

'ReadableStream' is not defined

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the version of Node Nock uses doesn't have the ReadableStream global. I'd double-check if this isn't the linter's problem as the first measure.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes the linter doesn't play well with relatively newly added globals. This can be modified in the linter's globals key, if talking about ESLint.

Copy link
Contributor Author

@mikicho mikicho Sep 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
I think we can fix this after we will solve the got error. Most of Nock's tests are based on it and I'm eager to solve it and run all tests to find cases that we haven't covered yet.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to look into the got issue this week based on my availability. Right now, the best next step is to reliably reproduce it in an automated test. I've already written one (see https://github.com/mswjs/interceptors/pull/432?notification_referrer_id=NT_kwDOAOSmz7M3NzYwMzQ3MDA1OjE0OTg0OTEx&notifications_query=is%3Aunread#issuecomment-1724139617) but you've provided some input that I need to reflect in the test.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed the got issue, would appreciate a quick review of the pull request!

start(controller) {
message.on('data', (chunk) => controller.enqueue(chunk))
message.on('end', () => controller.close())

/**
* @todo Should also listen to the "error" on the message
* and forward it to the controller. Otherwise the stream
* will pend indefinitely.
*/
},
})

return new Response(readable, {
status: message.statusCode,
statusText: message.statusMessage,
headers: createHeadersFromIncomingHttpHeaders(message.headers),
})
}

/**
* @param {IncomingHttpHeaders} httpHeaders
*/
function createHeadersFromIncomingHttpHeaders(httpHeaders) {
const headers = new Headers()

for (const headerName in httpHeaders) {
const headerValues = httpHeaders[headerName]

if (typeof headerValues === 'undefined') {
continue
}

if (Array.isArray(headerValues)) {
headerValues.forEach((headerValue) => {
headers.append(headerName, headerValue)
})

continue
}

headers.set(headerName, headerValues)
}

return headers
}

module.exports = { createResponse }
23 changes: 13 additions & 10 deletions lib/intercept.js
Expand Up @@ -13,7 +13,8 @@
const { BatchInterceptor } = require('@mswjs/interceptors')
const { FetchInterceptor } = require('@mswjs/interceptors/fetch')
const { default: nodeInterceptors } = require('@mswjs/interceptors/presets/node')
const { once } = require('events')

Check failure on line 16 in lib/intercept.js

View workflow job for this annotation

GitHub Actions / Lint JavaScript

'once' is assigned a value but never used
const { createResponse } = require('./create_response')

/**
* @name NetConnectNotAllowedError
Expand Down Expand Up @@ -380,10 +381,7 @@
headers: Object.fromEntries(fetchRequest.headers.entries())
};

const clientRequest = new http.ClientRequest(options);
// Note: You won't have access to the request body data from the Fetch Request

return clientRequest;
return new http.ClientRequest(options);
}

function activate() {
Expand All @@ -397,7 +395,8 @@
interceptors: [...nodeInterceptors, new FetchInterceptor()],
mikicho marked this conversation as resolved.
Show resolved Hide resolved
})
interceptor.apply();
interceptor.on('request', async function ({ request, requestId }) {
interceptor.on('request', function ({ request, requestId }) {
return new Promise(resolve => {
const { options, callback } = common.normalizeClientRequestArgs(request.url)
options.proto = options.protocol.slice(0, -1)
const interceptors = interceptorsFor(options)
Expand Down Expand Up @@ -425,24 +424,28 @@
throw new Error('TODO')
}

const req = convertFetchRequestToClientRequest(request);
req.on('response', response => {
request.respondWith(new Response('test', { status: 200 }))
const nockRequest = convertFetchRequestToClientRequest(request);
nockRequest.on('response', nockResponse => {
const response = createResponse(nockResponse)
nockResponse.on('end', () => {
request.respondWith(response)
resolve()
})
})

req.end()
await once(req, 'response')
nockRequest.end()
} else {
globalEmitter.emit('no match', options)
if (isOff() || isEnabledForNetConnect(options)) {
// TODO: implement unmocked
return overriddenRequest(options, callback)

Check failure on line 441 in lib/intercept.js

View workflow job for this annotation

GitHub Actions / Lint JavaScript

'overriddenRequest' is not defined
} else {
const error = new NetConnectNotAllowedError(options.host, options.path)
return new ErroringClientRequest(error)
}
}
})
})
}

module.exports = {
Expand Down