Skip to content

Commit 9dcf917

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

File tree

3 files changed

+155
-11
lines changed

3 files changed

+155
-11
lines changed

lib/test.js

+58-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
@@ -159,24 +159,61 @@ Test.prototype.assert = function(resError, res, fn) {
159159
ETIMEDOUT: 'Operation timed out'
160160
};
161161

162+
162163
if (!res && resError && (resError instanceof Error) && (resError.syscall === 'connect')
163164
&& (Object.getOwnPropertyNames(sysErrors).indexOf(resError.code) >= 0)) {
164-
error = new Error(resError.code + ': ' + sysErrors[resError.code]);
165-
fn.call(this, error, null);
165+
fn.call(
166+
this,
167+
new Error(resError.code + ': ' + sysErrors[resError.code]),
168+
null
169+
);
166170
return;
167171
}
168172

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

174210
// set unexpected superagent error if no other error has occurred.
175-
if (!error && resError instanceof Error && (!res || resError.status !== res.status)) {
176-
error = resError;
211+
if (resError instanceof Error && (!res || resError.status !== res.status)) {
212+
return fn.call(this, resError, res);
177213
}
178214

179-
fn.call(this, error || null, res);
215+
// no error
216+
fn.call(this, null, res);
180217
};
181218

182219
/**
@@ -282,7 +319,17 @@ Test.prototype._assertFunction = function(check, res) {
282319
} catch (e) {
283320
err = e;
284321
}
285-
if (err instanceof Error) return err;
322+
323+
// We got an error, return it.
324+
if (err instanceof Error) {
325+
return err;
326+
}
327+
328+
// We got a promise, return it and let the caller figure out if it contains
329+
// an error.
330+
if (err && typeof err.then === 'function') {
331+
return err;
332+
}
286333
};
287334

288335
/**

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

+95
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,100 @@ describe('request(app)', function() {
342343
});
343344
});
344345

346+
describe('.expect(fn)', function() {
347+
it('should handle a function', 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 function returning a promise', function() {
370+
var myFunction = function(res) {
371+
return new Promise(function(resolve, reject) {
372+
reject(new Error('rejected'));
373+
});
374+
};
375+
var app = express();
376+
377+
app.get('/', function(req, res) {
378+
res.send('hey');
379+
});
380+
381+
return request(app)
382+
.get('/')
383+
.expect(200)
384+
.expect(myFunction)
385+
.then(function(res) {
386+
throw new Error('Test passed.');
387+
})
388+
.catch(function(err) {
389+
err.message.should.equal('rejected');
390+
});
391+
});
392+
393+
it('should handle promises with callback resolution', function(done) {
394+
Promise.resolve(function(res) {
395+
res.text.should.equal('hey');
396+
throw new Error('Promise threw.');
397+
}).then(function(myPromise) {
398+
var app = express();
399+
400+
app.get('/', function(req, res) {
401+
res.send('hey');
402+
});
403+
404+
request(app)
405+
.get('/')
406+
.expect(200)
407+
.expect(myPromise)
408+
.end(function(err, res) {
409+
err.message.should.equal('Promise threw.');
410+
done();
411+
});
412+
});
413+
});
414+
415+
it('should handle promises with promise resolution', function() {
416+
return Promise.resolve(function(res) {
417+
res.text.should.equal('hey');
418+
throw new Error('Promise threw.');
419+
}).then(function(myPromise) {
420+
var app = express();
421+
422+
app.get('/', function(req, res) {
423+
res.send('hey');
424+
});
425+
426+
return request(app)
427+
.get('/')
428+
.expect(200)
429+
.expect(myPromise)
430+
.then(function(res) {
431+
throw new Error('Test passed.');
432+
})
433+
.catch(function(err) {
434+
err.message.should.equal('Promise threw.');
435+
});
436+
});
437+
});
438+
});
439+
345440
describe('.expect(status[, fn])', function() {
346441
it('should assert the response status', function(done) {
347442
var app = express();

0 commit comments

Comments
 (0)