Skip to content

Commit 1d88e30

Browse files
authored
Add more tests, minor compatibility changes (#8)
- On timeout, `readyState` is set to `DONE` - Writing an unsupported value to `responseType` now fails silently
1 parent 552faaf commit 1d88e30

File tree

2 files changed

+172
-23
lines changed

2 files changed

+172
-23
lines changed

lib/index.js

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ var https = require('https');
1313

1414
var NodeXHREventTarget = require('./node-xhr-event-target');
1515

16+
/**
17+
* Currently-supported response types.
18+
*
19+
* @private
20+
* @readonly
21+
* @type {Object<String, Boolean>}
22+
*/
23+
var supportedResponseTypes = Object.freeze({
24+
/** Text response (implicit) */
25+
'': true,
26+
/** Text response */
27+
'text': true
28+
});
29+
1630
/**
1731
* Makes a request using either `http.request` or `https.request`, depending
1832
* on the value of `opts.protocol`.
@@ -107,6 +121,16 @@ module.exports = function () {
107121
*/
108122
this._resp = null;
109123

124+
/**
125+
* The type of the response. Currently, only `''` and `'text'` are
126+
* supported, which both indicate the response should be a `String`.
127+
*
128+
* @private
129+
* @type {String}
130+
* @default ''
131+
*/
132+
this._responseType = '';
133+
110134
/**
111135
* The current response text, or `null` if the request hasn't been sent or
112136
* was unsuccessful.
@@ -241,7 +265,16 @@ NodeHttpXHR.prototype = Object.create(
241265
* @type {String}
242266
* @default ''
243267
*/
244-
responseType: { value: '', writable: true },
268+
responseType: {
269+
get: function () { return this._responseType; },
270+
set: function (responseType) {
271+
if (!(responseType in supportedResponseTypes)) {
272+
return;
273+
}
274+
275+
this._responseType = responseType;
276+
}
277+
},
245278
/**
246279
* The response, encoded according to {@link
247280
* module:node-http-xhr#responseType
@@ -260,7 +293,7 @@ NodeHttpXHR.prototype = Object.create(
260293
response: {
261294
get: function getResponse() {
262295
var type = this.responseType;
263-
if (type !== '' && type !== 'text') {
296+
if (!(type in supportedResponseTypes)) {
264297
throw new Error('Unsupported responseType "' + type + '"');
265298
}
266299

@@ -270,7 +303,7 @@ NodeHttpXHR.prototype = Object.create(
270303
/**
271304
* The response body as a string.
272305
*
273-
* If `send())` has not been called yet, this is `null`.
306+
* If `send()` has not been called yet, this is `null`.
274307
*
275308
* This will be incomplete until the response actually finishes.
276309
*
@@ -463,6 +496,7 @@ NodeHttpXHR.prototype.send = function (data) {
463496
req.on('aborted', onAbort);
464497

465498
req.on('timeout', function onTimeout() {
499+
this._setReadyState(this.DONE);
466500
this.dispatchEvent({
467501
type: 'timeout'
468502
});

test/index.test.js

Lines changed: 135 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function describeXHREvents() {
2424
describe('events', function () {
2525
describe('`readystatechange` event', function () {
2626
it('is fired whenever the ready state changes', function (done) {
27-
var txtInterceptor = nock('http://example.com')
27+
var txtScope = nock('http://example.com')
2828
.get('/test.txt')
2929
.reply(200, 'This is a test response.');
3030

@@ -45,7 +45,7 @@ function describeXHREvents() {
4545
}
4646

4747
assume(req.readyState).equals(req.DONE);
48-
txtInterceptor.done();
48+
txtScope.done();
4949
done();
5050
});
5151

@@ -74,20 +74,20 @@ function describeXHREvents() {
7474
});
7575

7676
describe('`error` event', function () {
77-
var errInterceptor;
77+
var errScope;
7878
var errMessage = 'Test error message';
7979
var errCode = 'TEST_ERROR';
8080

8181
beforeEach(function () {
82-
errInterceptor = nock('http://example.com')
82+
errScope = nock('http://example.com')
8383
.get('/error')
8484
.replyWithError({ message: errMessage, code: errCode });
8585
});
8686

8787
it('is fired if the request encountered an error', function (done) {
8888
var spy = sinon.spy(function () {
8989
assume(spy).is.called(1);
90-
errInterceptor.done();
90+
errScope.done();
9191
done();
9292
});
9393

@@ -103,7 +103,7 @@ function describeXHREvents() {
103103
assume(spy).is.called(1);
104104
assume(err.message).equals(errMessage);
105105
assume(err.code).equals(errCode);
106-
errInterceptor.done();
106+
errScope.done();
107107
done();
108108
});
109109

@@ -120,14 +120,14 @@ function describeXHREvents() {
120120

121121
describe('`timeout` event', function () {
122122
it('is fired if the request timed out', function (done) {
123-
var timeoutInterceptor = nock('http://example.com')
123+
var timeoutScope = nock('http://example.com')
124124
.get('/timeout')
125125
.socketDelay(1000)
126126
.reply(204);
127127

128128
var spy = sinon.spy(function () {
129129
assume(spy).is.called(1);
130-
timeoutInterceptor.done();
130+
timeoutScope.done();
131131
done();
132132
});
133133

@@ -141,15 +141,15 @@ function describeXHREvents() {
141141
describe('`load` event', function () {
142142
it('is fired once the request loads', function (done) {
143143
var txtResponse = 'This is a test response.';
144-
var txtInterceptor = nock('http://example.com')
144+
var txtScope = nock('http://example.com')
145145
.get('/test.txt')
146146
.reply(200, txtResponse);
147147

148148
var spy = sinon.spy(function () {
149149
assume(spy).is.called(1);
150150
assume(req.readyState).equals(req.DONE);
151151
assume(req.responseText).equals(txtResponse);
152-
txtInterceptor.done();
152+
txtScope.done();
153153
done();
154154
});
155155

@@ -264,7 +264,7 @@ function describeXHRProps() {
264264

265265
it('changes to `DONE` once response is fully loaded', function (done) {
266266
var txtResponse = 'This is a test response.';
267-
var txtInterceptor = nock('http://example.com')
267+
var txtScope = nock('http://example.com')
268268
.get('/test.txt')
269269
.reply(200, txtResponse);
270270

@@ -287,7 +287,7 @@ function describeXHRProps() {
287287
assume(req.readyState).equals(req.DONE);
288288
assume(txtResponse).equals(req.responseText);
289289

290-
txtInterceptor.done();
290+
txtScope.done();
291291
done();
292292
});
293293

@@ -297,11 +297,11 @@ function describeXHRProps() {
297297
});
298298

299299
describe('#status', function () {
300-
var txtInterceptor;
300+
var emptyScope;
301301

302302
beforeEach(function () {
303-
txtInterceptor = nock('http://example.com')
304-
.get('/test.txt')
303+
emptyScope = nock('http://example.com')
304+
.get('/empty')
305305
.reply(204);
306306
});
307307

@@ -312,14 +312,14 @@ function describeXHRProps() {
312312
assume(req.status).equals(0);
313313
}
314314
if (req.readyState === req.DONE) {
315-
txtInterceptor.done();
315+
emptyScope.done();
316316
done();
317317
return;
318318
}
319319
});
320320

321321
req.onreadystatechange = spy;
322-
req.open('GET', 'http://example.com/test.txt');
322+
req.open('GET', 'http://example.com/empty');
323323
req.send();
324324
});
325325

@@ -330,13 +330,128 @@ function describeXHRProps() {
330330
assume(req.status).equals(204);
331331
}
332332
if (req.readyState === req.DONE) {
333-
txtInterceptor.done();
333+
emptyScope.done();
334+
done();
335+
return;
336+
}
337+
});
338+
339+
req.onreadystatechange = spy;
340+
req.open('GET', 'http://example.com/empty');
341+
req.send();
342+
});
343+
});
344+
345+
describe('#statusText', function () {
346+
it('is initially `\'\'`', function () {
347+
assume(req.statusText).equals('');
348+
});
349+
350+
xit('contains status text once headers are received', function (done) {
351+
var emptyScope = nock('http://example.com')
352+
.get('/empty')
353+
.reply(204);
354+
355+
var spy = sinon.spy(function () {
356+
assume(spy.callCount).is.at.most(2);
357+
if (req.readyState >= req.HEADERS_RECEIVED) {
358+
// TODO: nock doesn't let us specify the response status text
359+
// for now, use `null` because that's what `http.IncomingMessage`
360+
// uses for `#statusMessage` if the response doesn't have one
361+
assume(req.statusText).equals(null);
362+
emptyScope.done();
334363
done();
335364
return;
336365
}
337366
});
338367

339368
req.onreadystatechange = spy;
369+
req.open('GET', 'http://example.com/empty');
370+
});
371+
});
372+
373+
describe('#timeout', function () {
374+
var timeout = 50;
375+
var timeoutScope;
376+
377+
beforeEach(function () {
378+
timeoutScope = nock('http://example.com')
379+
.get('/timeout')
380+
.socketDelay(5000)
381+
.reply(204);
382+
});
383+
384+
it('is initially 0', function () {
385+
assume(req.timeout).equals(0);
386+
});
387+
388+
it('times out the request if exceeded', function (done) {
389+
var spy = sinon.spy(function () {
390+
assume(spy).is.called(1);
391+
timeoutScope.done();
392+
done();
393+
});
394+
395+
req.ontimeout = spy;
396+
req.timeout = timeout;
397+
req.open('GET', 'http://example.com/timeout');
398+
req.send();
399+
});
400+
401+
it('times out even if set after request is sent', function (done) {
402+
var spy = sinon.spy(function () {
403+
assume(spy).is.called(1);
404+
timeoutScope.done();
405+
done();
406+
});
407+
408+
req.ontimeout = spy;
409+
req.open('GET', 'http://example.com/timeout');
410+
req.send();
411+
req.timeout = timeout;
412+
});
413+
});
414+
415+
describe('#responseType', function () {
416+
it('intially is `\'\'`', function () {
417+
assume(req.responseType).equals('');
418+
});
419+
420+
it('silently fails to write if new value is unsupported', function () {
421+
req.responseType = null;
422+
assume(req.responseType).equals('');
423+
});
424+
425+
it('allows supported values to be written', function () {
426+
req.responseType = 'text';
427+
assume(req.responseType).equals('text');
428+
req.responseType = '';
429+
assume(req.responseType).equals('');
430+
});
431+
});
432+
433+
describe('#response', function () {
434+
var txtResponse = 'This is a test response.';
435+
var txtScope;
436+
beforeEach(function () {
437+
txtScope = nock('http://example.com')
438+
.get('/test.txt')
439+
.reply(200, txtResponse);
440+
});
441+
442+
it('initially is `null`', function () {
443+
assume(req.response).equals(null);
444+
});
445+
446+
it('supports `responseType = \'text\'`', function (done) {
447+
var spy = sinon.spy(function () {
448+
assume(spy).is.called(1);
449+
assume(req.response).equals(txtResponse);
450+
txtScope.done();
451+
done();
452+
});
453+
454+
req.onload = spy;
340455
req.open('GET', 'http://example.com/test.txt');
341456
req.send();
342457
});
@@ -379,7 +494,7 @@ describe('XMLHttpRequest', function () {
379494
['http:', 'https:'].forEach(function (protocol) {
380495
it('works with protocol `' + protocol + '`', function (done) {
381496
var txtResponse = 'This is a test response.';
382-
var txtInterceptor = nock(protocol + '//example.com')
497+
var txtScope = nock(protocol + '//example.com')
383498
.get('/test.txt')
384499
.reply(200, txtResponse);
385500

@@ -391,7 +506,7 @@ describe('XMLHttpRequest', function () {
391506
assume(req.response).equals(txtResponse);
392507
assume(req.responseText).equals(txtResponse);
393508
assume(req.status).equals(200);
394-
txtInterceptor.done();
509+
txtScope.done();
395510
done();
396511
});
397512

0 commit comments

Comments
 (0)