Skip to content

Commit 187db85

Browse files
TLPcoderpaulmelnikow
authored andcommitted
feat: Add lifecycle function to clear pending timeouts (#1748)
Closes #1118
1 parent b7f9f13 commit 187db85

File tree

10 files changed

+88
-11
lines changed

10 files changed

+88
-11
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,14 @@ You can cleanup all the prepared mocks (could be useful to cleanup some state af
995995
nock.cleanAll()
996996
```
997997

998+
### .abortPendingRequests()
999+
1000+
You can abort all current pending request like this:
1001+
1002+
```js
1003+
nock.abortPendingRequests()
1004+
```
1005+
9981006
### .persist()
9991007

10001008
You can make all the interceptors for a scope persist by calling `.persist()` on it:

index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const {
1212
disableNetConnect,
1313
enableNetConnect,
1414
removeAll,
15+
abortPendingRequests,
1516
} = require('./lib/intercept')
1617
const recorder = require('./lib/recorder')
1718
const { Scope, load, loadDefs, define } = require('./lib/scope')
@@ -35,6 +36,7 @@ Object.assign(module.exports, {
3536
removeAll()
3637
return module.exports
3738
},
39+
abortPendingRequests,
3840
load,
3941
loadDefs,
4042
define,

lib/common.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const _ = require('lodash')
44
const debug = require('debug')('nock.common')
55
const url = require('url')
6+
const timers = require('timers')
67

78
/**
89
* Normalizes the request options so that it always has `host` property.
@@ -593,6 +594,32 @@ function deepEqual(expected, actual) {
593594
return expected === actual
594595
}
595596

597+
const timeouts = []
598+
const intervals = []
599+
const immediates = []
600+
601+
const wrapTimer = (timer, ids) => (...args) => {
602+
const id = timer(...args)
603+
ids.push(id)
604+
return id
605+
}
606+
607+
const setTimeout = wrapTimer(timers.setTimeout, timeouts)
608+
const setInterval = wrapTimer(timers.setInterval, intervals)
609+
const setImmediate = wrapTimer(timers.setImmediate, immediates)
610+
611+
function clearTimer(clear, ids) {
612+
while (ids.length) {
613+
clear(ids.shift())
614+
}
615+
}
616+
617+
function removeAllTimers() {
618+
clearTimer(clearTimeout, timeouts)
619+
clearTimer(clearInterval, intervals)
620+
clearTimer(clearImmediate, immediates)
621+
}
622+
596623
exports.normalizeClientRequestArgs = normalizeClientRequestArgs
597624
exports.normalizeRequestOptions = normalizeRequestOptions
598625
exports.normalizeOrigin = normalizeOrigin
@@ -615,3 +642,7 @@ exports.matchStringOrRegexp = matchStringOrRegexp
615642
exports.formatQueryValue = formatQueryValue
616643
exports.isStream = isStream
617644
exports.dataEqual = dataEqual
645+
exports.setTimeout = setTimeout
646+
exports.setInterval = setInterval
647+
exports.setImmediate = setImmediate
648+
exports.removeAllTimers = removeAllTimers

lib/delayed_body.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module.exports = class DelayedBody extends Transform {
3434

3535
// TODO: This would be more readable if the stream case were moved into
3636
// the `if` statement above.
37-
setTimeout(function() {
37+
common.setTimeout(function() {
3838
if (common.isStream(body) && !ended) {
3939
body.once('end', function() {
4040
self.end(data)

lib/intercept.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const http = require('http')
1111
const _ = require('lodash')
1212
const debug = require('debug')('nock.intercept')
1313
const globalEmitter = require('./global_emitter')
14-
const timers = require('timers')
1514

1615
/**
1716
* @name NetConnectNotAllowedError
@@ -293,7 +292,7 @@ function overrideClientRequest() {
293292
if (isOff() || isEnabledForNetConnect(options)) {
294293
originalClientRequest.apply(this, arguments)
295294
} else {
296-
timers.setImmediate(
295+
common.setImmediate(
297296
function() {
298297
const error = new NetConnectNotAllowedError(
299298
options.host,
@@ -444,4 +443,5 @@ module.exports = {
444443
disableNetConnect,
445444
overrideClientRequest,
446445
restoreOverriddenClientRequest,
446+
abortPendingRequests: common.removeAllTimers,
447447
}

lib/intercepted_request_router.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const {
88
} = require('http')
99
const { request: originalHttpsRequest } = require('https')
1010
const propagate = require('propagate')
11-
const timers = require('timers')
1211
const common = require('./common')
1312
const globalEmitter = require('./global_emitter')
1413
const Socket = require('./socket')
@@ -74,7 +73,7 @@ class InterceptedRequestRouter {
7473

7574
// https://github.com/nock/nock/issues/256
7675
if (options.headers.expect === '100-continue') {
77-
timers.setImmediate(() => {
76+
common.setImmediate(() => {
7877
debug('continue')
7978
req.emit('continue')
8079
})
@@ -121,7 +120,7 @@ class InterceptedRequestRouter {
121120
this.emitError(new Error('Request aborted'))
122121
}
123122

124-
timers.setImmediate(function() {
123+
common.setImmediate(function() {
125124
req.emit('drain')
126125
})
127126

lib/playback_interceptor.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict'
22

33
const util = require('util')
4-
const timers = require('timers')
54
const zlib = require('zlib')
65
const debug = require('debug')('nock.playback_interceptor')
76
const _ = require('lodash')
@@ -106,7 +105,7 @@ function playbackInterceptor({
106105
} else {
107106
error = new Error(interceptor.errorMessage)
108107
}
109-
timers.setTimeout(() => emitError(error), interceptor.getTotalDelay())
108+
common.setTimeout(() => emitError(error), interceptor.getTotalDelay())
110109
return
111110
}
112111

@@ -311,13 +310,13 @@ function playbackInterceptor({
311310
}
312311

313312
// Stream the response chunks one at a time.
314-
timers.setImmediate(function emitChunk() {
313+
common.setImmediate(function emitChunk() {
315314
const chunk = responseBuffers.shift()
316315

317316
if (chunk) {
318317
debug('emitting response chunk')
319318
response.push(chunk)
320-
timers.setImmediate(emitChunk)
319+
common.setImmediate(emitChunk)
321320
} else {
322321
debug('ending response stream')
323322
response.push(null)
@@ -338,7 +337,7 @@ function playbackInterceptor({
338337
interceptor.delayConnectionInMs > 0
339338
) {
340339
socket.applyDelay(interceptor.delayConnectionInMs)
341-
setTimeout(respond, interceptor.delayConnectionInMs)
340+
common.setTimeout(respond, interceptor.delayConnectionInMs)
342341
} else {
343342
respond()
344343
}

tests/test_common.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const http = require('http')
44
const { test } = require('tap')
55
const common = require('../lib/common')
66
const matchBody = require('../lib/match_body')
7+
const sinon = require('sinon')
78
const nock = require('..')
89

910
require('./cleanup_after_each')()
@@ -486,3 +487,21 @@ test('normalizeClientRequestArgs with a single callback', async t => {
486487
t.deepEqual(options, {})
487488
t.is(callback, cb)
488489
})
490+
491+
test('testing timers are deleted correctly', t => {
492+
const timeoutSpy = sinon.spy()
493+
const intervalSpy = sinon.spy()
494+
const immediateSpy = sinon.spy()
495+
496+
common.setTimeout(timeoutSpy, 0)
497+
common.setInterval(intervalSpy, 0)
498+
common.setImmediate(immediateSpy)
499+
common.removeAllTimers()
500+
501+
setImmediate(() => {
502+
t.equal(timeoutSpy.called, false)
503+
t.equal(intervalSpy.called, false)
504+
t.equal(immediateSpy.called, false)
505+
t.end()
506+
})
507+
})

tests/test_nock_lifecycle.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const http = require('http')
44
const { test } = require('tap')
55
const nock = require('..')
6+
const sinon = require('sinon')
67
const got = require('./got_client')
78

89
require('./cleanup_after_each')()
@@ -182,3 +183,20 @@ test('resetting nock catastrophically while a request is in progress is handled
182183
t.equal(body, 'hi')
183184
scope.done()
184185
})
186+
187+
test('abort pending request when abortPendingRequests is called', t => {
188+
const reqSpy = sinon.spy()
189+
190+
nock('http://example.test')
191+
.get('/')
192+
.delayConnection(100)
193+
.reply(200, 'OK')
194+
195+
http.get('http://example.test', reqSpy)
196+
197+
setTimeout(() => {
198+
t.equal(reqSpy.called, false)
199+
t.end()
200+
}, 200)
201+
process.nextTick(nock.abortPendingRequests)
202+
})

types/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ declare namespace nock {
2626
function loadDefs(path: string): Definition[]
2727
function define(defs: Definition[]): Scope[]
2828
function restore(): void
29+
function abortPendingRequests(): void
2930

3031
let back: Back
3132
let emitter: NodeJS.EventEmitter

0 commit comments

Comments
 (0)