Skip to content

Commit 77aaded

Browse files
committed
Add support for promises / async functions in expect. Fixes #401.
1 parent 199506d commit 77aaded

File tree

3 files changed

+207
-11
lines changed

3 files changed

+207
-11
lines changed

lib/test.js

+64-11
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ Test.prototype.serverAddress = function(app, path) {
7979
*/
8080

8181
Test.prototype.expect = function(a, b, c) {
82-
// callback
83-
if (typeof a === 'function') {
82+
// callback or promise
83+
if (typeof a === 'function' || typeof a.then === 'function') {
8484
this._asserts.push(a);
8585
return this;
8686
}
@@ -145,7 +145,7 @@ Test.prototype.end = function(fn) {
145145
*/
146146

147147
Test.prototype.assert = function(resError, res, fn) {
148-
var error;
148+
var maybePromise;
149149
var i;
150150

151151
// check for unexpected network errors or server not running/reachable errors
@@ -161,22 +161,65 @@ Test.prototype.assert = function(resError, res, fn) {
161161

162162
if (!res && resError && (resError instanceof Error) && (resError.syscall === 'connect')
163163
&& (Object.getOwnPropertyNames(sysErrors).indexOf(resError.code) >= 0)) {
164-
error = new Error(resError.code + ': ' + sysErrors[resError.code]);
165-
fn.call(this, error, null);
164+
fn.call(
165+
this,
166+
new Error(resError.code + ': ' + sysErrors[resError.code]),
167+
null
168+
);
166169
return;
167170
}
168171

169172
// asserts
170-
for (i = 0; i < this._asserts.length && !error; i += 1) {
171-
error = this._assertFunction(this._asserts[i], res);
173+
for (i = 0; i < this._asserts.length; i += 1) {
174+
// handle promises.
175+
if (typeof this._asserts[i].then === 'function') {
176+
this._asserts[i]
177+
.then(res)
178+
.catch(function(promiseError) {
179+
return fn.call(this, promiseError, res);
180+
})
181+
.then(function(maybeError) {
182+
if (maybeError instanceof Error) {
183+
// promise resolved to an error
184+
return fn.call(this, maybeError, res);
185+
}
186+
// promise resolved to a non-error
187+
return fn.call(this, null, res);
188+
});
189+
return;
190+
}
191+
192+
// handle functions
193+
maybePromise = this._assertFunction(this._asserts[i], res);
194+
if (maybePromise && typeof maybePromise.then === 'function') {
195+
// function returned a promise
196+
maybePromise
197+
.then(function(maybeError) { // eslint-disable-line no-loop-func
198+
if (maybeError instanceof Error) {
199+
// promise resolved to an error
200+
return fn.call(this, maybeError, res);
201+
}
202+
// promise resolved to a non-error
203+
return fn.call(this, null, res);
204+
})
205+
.catch(function(promiseError) {
206+
// error resolving the promise.
207+
return fn.call(this, promiseError, res);
208+
});
209+
return;
210+
} else if (maybePromise instanceof Error) {
211+
// function returned a non-promise. if it is an error, report it.
212+
return fn.call(this, maybePromise, res);
213+
}
172214
}
173215

174216
// set unexpected superagent error if no other error has occurred.
175-
if (!error && resError instanceof Error && (!res || resError.status !== res.status)) {
176-
error = resError;
217+
if (resError instanceof Error && (!res || resError.status !== res.status)) {
218+
return fn.call(this, resError, res);
177219
}
178220

179-
fn.call(this, error || null, res);
221+
// no error
222+
fn.call(this, null, res);
180223
};
181224

182225
/**
@@ -282,7 +325,17 @@ Test.prototype._assertFunction = function(check, res) {
282325
} catch (e) {
283326
err = e;
284327
}
285-
if (err instanceof Error) return err;
328+
329+
// We got an error, return it.
330+
if (err instanceof Error) {
331+
return err;
332+
}
333+
334+
// We got a promise, return it and let the caller figure out if it contains
335+
// an error.
336+
if (err && typeof err.then === 'function') {
337+
return err;
338+
}
286339
};
287340

288341
/**

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"methods": "~1.1.2"
1313
},
1414
"devDependencies": {
15+
"bluebird": "^3.4.7",
1516
"body-parser": "~1.16.0",
1617
"cookie-parser": "~1.4.1",
1718
"eslint": "^3.14.1",
@@ -21,6 +22,7 @@
2122
"eslint-plugin-react": "6.4.1",
2223
"express": "~4.14.0",
2324
"mocha": "~3.2.0",
25+
"native-or-bluebird": "^1.2.0",
2426
"should": "~11.2.0"
2527
},
2628
"engines": {

test/supertest.js

+141
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var should = require('should');
66
var express = require('express');
77
var bodyParser = require('body-parser');
88
var cookieParser = require('cookie-parser');
9+
var Promise = require('native-or-bluebird');
910

1011
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
1112

@@ -342,6 +343,146 @@ describe('request(app)', function() {
342343
});
343344
});
344345

346+
describe('.expect(fn)', function() {
347+
it('should handle a function failing', function() {
348+
var myFunction = function(res) {
349+
res.text.should.equal('not hey');
350+
};
351+
var app = express();
352+
353+
app.get('/', function(req, res) {
354+
res.send('hey');
355+
});
356+
357+
return request(app)
358+
.get('/')
359+
.expect(200)
360+
.expect(myFunction)
361+
.then(function(res) {
362+
throw new Error('Test passed.');
363+
})
364+
.catch(function(err) {
365+
err.message.should.equal('expected \'hey\' to be \'not hey\'');
366+
});
367+
});
368+
369+
it('should handle a function passing', function() {
370+
var myFunction = function(res) {
371+
res.text.should.equal('not hey');
372+
};
373+
var app = express();
374+
375+
app.get('/', function(req, res) {
376+
res.send('hey');
377+
});
378+
379+
return request(app)
380+
.get('/')
381+
.expect(200)
382+
.expect(myFunction)
383+
.then(function(res) {
384+
res.text.should.equal('hey');
385+
})
386+
.catch(function(err) {
387+
(err === null).should.be.true;
388+
});
389+
});
390+
391+
it('should handle function returning a failing promise', function() {
392+
var myFunction = function(res) {
393+
return new Promise(function(resolve, reject) {
394+
reject(new Error('rejected'));
395+
});
396+
};
397+
var app = express();
398+
399+
app.get('/', function(req, res) {
400+
res.send('hey');
401+
});
402+
403+
return request(app)
404+
.get('/')
405+
.expect(200)
406+
.expect(myFunction)
407+
.then(function(res) {
408+
throw new Error('Test passed.');
409+
})
410+
.catch(function(err) {
411+
err.message.should.equal('rejected');
412+
});
413+
});
414+
415+
it('should handle function returning a passing promise', function() {
416+
var myFunction = function(res) {
417+
return new Promise(function(resolve, reject) {
418+
resolve(null);
419+
});
420+
};
421+
var app = express();
422+
423+
app.get('/', function(req, res) {
424+
res.send('hey');
425+
});
426+
427+
return request(app)
428+
.get('/')
429+
.expect(200)
430+
.expect(myFunction)
431+
.then(function(res) {
432+
res.text.should.equal('hey');
433+
})
434+
.catch(function(err) {
435+
(err === null).should.be.true;
436+
});
437+
});
438+
439+
it('should handle promises with callback resolution', function(done) {
440+
Promise.resolve(function(res) {
441+
res.text.should.equal('hey');
442+
throw new Error('Promise threw.');
443+
}).then(function(myPromise) {
444+
var app = express();
445+
446+
app.get('/', function(req, res) {
447+
res.send('hey');
448+
});
449+
450+
request(app)
451+
.get('/')
452+
.expect(200)
453+
.expect(myPromise)
454+
.end(function(err, res) {
455+
err.message.should.equal('Promise threw.');
456+
done();
457+
});
458+
});
459+
});
460+
461+
it('should handle promises with promise resolution', function() {
462+
return Promise.resolve(function(res) {
463+
res.text.should.equal('hey');
464+
throw new Error('Promise threw.');
465+
}).then(function(myPromise) {
466+
var app = express();
467+
468+
app.get('/', function(req, res) {
469+
res.send('hey');
470+
});
471+
472+
return request(app)
473+
.get('/')
474+
.expect(200)
475+
.expect(myPromise)
476+
.then(function(res) {
477+
throw new Error('Test passed.');
478+
})
479+
.catch(function(err) {
480+
err.message.should.equal('Promise threw.');
481+
});
482+
});
483+
});
484+
});
485+
345486
describe('.expect(status[, fn])', function() {
346487
it('should assert the response status', function(done) {
347488
var app = express();

0 commit comments

Comments
 (0)