Skip to content

Commit

Permalink
[Breaking] support passing in an async function for the test callback
Browse files Browse the repository at this point in the history
Merge pull request #472 from substack/async-await
  • Loading branch information
ljharb committed Dec 18, 2019
2 parents c3924d3 + 197019c commit 5f88895
Show file tree
Hide file tree
Showing 11 changed files with 407 additions and 1 deletion.
8 changes: 8 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,12 @@
"named": "never",
}],
},
"overrides": [
{
"files": ["test/async-await/*"],
"parserOptions": {
"ecmaVersion": 2017,
},
},
],
}
21 changes: 20 additions & 1 deletion lib/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,26 @@ Test.prototype.run = function () {
if (this._timeout != null) {
this.timeoutAfter(this._timeout);
}
this._cb(this);

var callbackReturn = this._cb(this);

if (
typeof Promise === 'function' &&
callbackReturn &&
typeof callbackReturn.then === 'function'
) {
var self = this;
Promise.resolve(callbackReturn).then(function onResolve() {
if (!self.calledEnd) {
self.end();
}
}).catch(function onError(err) {
self.fail(err);
self.end();
});
return;
}

this.emit('run');
};

Expand Down
231 changes: 231 additions & 0 deletions test/async-await.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
var tap = require('tap');

var stripFullStack = require('./common').stripFullStack;
var runProgram = require('./common').runProgram;

var nodeVersion = process.versions.node;
var majorVersion = nodeVersion.split('.')[0];

if (Number(majorVersion) < 8) {
process.exit(0);
}

tap.test('async1', function (t) {
runProgram('async-await', 'async1.js', function (r) {
t.same(r.stdout.toString('utf8'), [
'TAP version 13',
'# async1',
'ok 1 before await',
'ok 2 after await',
'',
'1..2',
'# tests 2',
'# pass 2',
'',
'# ok'
].join('\n') + '\n\n');
t.same(r.exitCode, 0);
t.same(r.stderr.toString('utf8'), '');
t.end();
});
});

tap.test('async2', function (t) {
runProgram('async-await', 'async2.js', function (r) {
var stdout = r.stdout.toString('utf8');
var lines = stdout.split('\n');
lines = lines.filter(function (line) {
return ! /^(\s+)at(\s+)<anonymous>$/.test(line);
});
stdout = lines.join('\n');

t.same(stripFullStack(stdout), [
'TAP version 13',
'# async2',
'ok 1 before await',
'not ok 2 after await',
' ---',
' operator: ok',
' expected: true',
' actual: false',
' at: Test.myTest ($TEST/async-await/async2.js:$LINE:$COL)',
' stack: |-',
' Error: after await',
' [... stack stripped ...]',
' at Test.myTest ($TEST/async-await/async2.js:$LINE:$COL)',
' ...',
'',
'1..2',
'# tests 2',
'# pass 1',
'# fail 1'
].join('\n') + '\n\n');
t.same(r.exitCode, 1);
t.same(r.stderr.toString('utf8'), '');
t.end();
});
});

tap.test('async3', function (t) {
runProgram('async-await', 'async3.js', function (r) {
t.same(r.stdout.toString('utf8'), [
'TAP version 13',
'# async3',
'ok 1 before await',
'ok 2 after await',
'',
'1..2',
'# tests 2',
'# pass 2',
'',
'# ok'
].join('\n') + '\n\n');
t.same(r.exitCode, 0);
t.same(r.stderr.toString('utf8'), '');
t.end();
});
});

tap.test('async4', function (t) {
runProgram('async-await', 'async4.js', function (r) {
t.same(stripFullStack(r.stdout.toString('utf8')), [
'TAP version 13',
'# async4',
'ok 1 before await',
'not ok 2 Error: oops',
' ---',
' operator: error',
' expected: |-',
' undefined',
' actual: |-',
' [Error: oops]',
' at: Test.myTest ($TEST/async-await/async4.js:$LINE:$COL)',
' stack: |-',
' Error: oops',
' at Timeout.myTimeout [as _onTimeout] ($TEST/async-await/async4.js:$LINE:$COL)',
' [... stack stripped ...]',
' ...',
'',
'1..2',
'# tests 2',
'# pass 1',
'# fail 1'
].join('\n') + '\n\n');
t.same(r.exitCode, 1);
t.same(r.stderr.toString('utf8'), '');
t.end();
});
});

tap.test('async5', function (t) {
runProgram('async-await', 'async5.js', function (r) {
t.same(stripFullStack(r.stdout.toString('utf8')), [
'TAP version 13',
'# async5',
'ok 1 before server',
'ok 2 after server',
'ok 3 before request',
'ok 4 after request',
'ok 5 should be equal',
'ok 6 should be equal',
'ok 7 undefined',
'not ok 8 .end() called twice',
' ---',
' operator: fail',
' at: Server.<anonymous> ($TEST/async-await/async5.js:$LINE:$COL)',
' stack: |-',
' Error: .end() called twice',
' [... stack stripped ...]',
' at Server.<anonymous> ($TEST/async-await/async5.js:$LINE:$COL)',
' [... stack stripped ...]',
' ...',
'',
'1..8',
'# tests 8',
'# pass 7',
'# fail 1'
].join('\n') + '\n\n');
t.same(r.exitCode, 1);
t.same(r.stderr.toString('utf8'), '');
t.end();
});
});

tap.test('sync-error', function (t) {
runProgram('async-await', 'sync-error.js', function (r) {
t.same(stripFullStack(r.stdout.toString('utf8')), [
'TAP version 13',
'# sync-error',
'ok 1 before throw',
''
].join('\n'));
t.same(r.exitCode, 1);

var stderr = r.stderr.toString('utf8');
var lines = stderr.split('\n');
lines = lines.filter(function (line) {
return ! /\(timers.js:/.test(line) &&
! /\(internal\/timers.js:/.test(line) &&
! /Immediate\.next/.test(line);
});
stderr = lines.join('\n');

t.same(stripFullStack(stderr), [
'$TEST/async-await/sync-error.js:5',
' throw new Error(\'oopsie\');',
' ^',
'',
'Error: oopsie',
' at Test.myTest ($TEST/async-await/sync-error.js:$LINE:$COL)',
' at Test.bound [as _cb] ($TAPE/lib/test.js:$LINE:$COL)',
' at Test.run ($TAPE/lib/test.js:$LINE:$COL)',
' at Test.bound [as run] ($TAPE/lib/test.js:$LINE:$COL)',
''
].join('\n'));
t.end();
});
});

tap.test('async-error', function (t) {
runProgram('async-await', 'async-error.js', function (r) {
var stdout = r.stdout.toString('utf8');
var lines = stdout.split('\n');
lines = lines.filter(function (line) {
return ! /^(\s+)at(\s+)<anonymous>$/.test(line);
});
stdout = lines.join('\n');

t.same(stripFullStack(stdout.toString('utf8')), [
'TAP version 13',
'# async-error',
'ok 1 before throw',
'not ok 2 Error: oopsie',
' ---',
' operator: fail',
' stack: |-',
' Error: Error: oopsie',
' [... stack stripped ...]',
' ...',
'',
'1..2',
'# tests 2',
'# pass 1',
'# fail 1',
'',
'',
].join('\n'));
t.same(r.exitCode, 1);

var stderr = r.stderr.toString('utf8');
var lines = stderr.split('\n');
lines = lines.filter(function (line) {
return ! /\(timers.js:/.test(line) &&
! /\(internal\/timers.js:/.test(line) &&
! /Immediate\.next/.test(line);
});
stderr = lines.join('\n');

t.same(stderr, '');
t.end();
});
});
7 changes: 7 additions & 0 deletions test/async-await/async-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
var test = require('../../');

test('async-error', async function myTest(t) {
t.ok(true, 'before throw');
throw new Error('oopsie');
t.ok(true, 'after throw');
});
14 changes: 14 additions & 0 deletions test/async-await/async1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var test = require('../../');

test('async1', async function myTest(t) {
try {
t.ok(true, 'before await');
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
t.ok(true, 'after await');
t.end();
} catch (err) {
t.ifError(err);
}
});
13 changes: 13 additions & 0 deletions test/async-await/async2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var test = require('../../');

test('async2', async function myTest(t) {
try {
t.ok(true, 'before await');
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
t.ok(false, 'after await');
} catch (err) {
t.ifError(err);
}
});
9 changes: 9 additions & 0 deletions test/async-await/async3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
var test = require('../../');

test('async3', async function myTest(t) {
t.ok(true, 'before await');
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
t.ok(true, 'after await');
});
15 changes: 15 additions & 0 deletions test/async-await/async4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
var test = require('../../');

test('async4', async function myTest(t) {
try {
t.ok(true, 'before await');
await new Promise((resolve, reject) => {
setTimeout(function myTimeout() {
reject(new Error('oops'));
}, 10);
});
t.ok(true, 'after await');
} catch (err) {
t.ifError(err);
}
});
57 changes: 57 additions & 0 deletions test/async-await/async5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
var util = require('util');
var http = require('http');

var test = require('../../');

test('async5', async function myTest(t) {
try {
t.ok(true, 'before server');

var mockDb = { state: 'old' };
var server = http.createServer(function (req, res) {
res.end('OK');

// Pretend we write to the DB and it takes time.
setTimeout(function () {
mockDb.state = 'new';
}, 10);
});

await util.promisify(function (cb) {
server.listen(0, cb);
})();

t.ok(true, 'after server');

t.ok(true, 'before request');

var res = await util.promisify(function (cb) {
var req = http.request({
hostname: 'localhost',
port: server.address().port,
path: '/',
method: 'GET'
}, function (res) {
cb(null, res);
});
req.end();
})();

t.ok(true, 'after request');

res.resume();
t.equal(res.statusCode, 200);

setTimeout(function () {
t.equal(mockDb.state, 'new');

server.close(function (err) {
t.ifError(err);
t.end();
});
}, 50);
} catch (err) {
t.ifError(err);
t.end();
}
});
Loading

0 comments on commit 5f88895

Please sign in to comment.