├── .gitignore ├── .travis.yml ├── bin └── copy-tape-tests.js ├── example.js ├── index.js ├── manager.js ├── package.json ├── readme.md └── test ├── catch-exceptions.js └── catch-exceptions ├── catch-harness.js └── catch.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/* 3 | !test/catch-exceptions* 4 | 5 | *.log 6 | *.swp 7 | .DS* 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | - "4.0" 5 | - "0.12" 6 | - "0.11" 7 | - "0.10" -------------------------------------------------------------------------------- /bin/copy-tape-tests.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | var mkdirp = require('mkdirp') 5 | var cpr = require('cpr') 6 | 7 | var src = path.resolve(__dirname, '../node_modules/tape/test') 8 | var dest = path.resolve(__dirname, '../test') 9 | 10 | mkdirp(dest, function (err) { 11 | if (err) process.exit(1) 12 | cpr(src, dest, function (err, files) { 13 | process.exit(err ? 1 : 0) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var test = require('./') 2 | 3 | test('cause an exception', function (assert) { 4 | asdf 5 | }) 6 | 7 | test('still run this test', function (assert) { 8 | assert.equal(1 + 1, 2, 'this still ran') 9 | assert.end() 10 | }) 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var tape = require('tape') 2 | var global = require('global') 3 | var tests = require('./manager')() 4 | var harness 5 | 6 | exports = module.exports = tape 7 | 8 | // Maintain tape@1 compatibility 9 | var _end = ( 10 | exports.Test.prototype._end || 11 | exports.Test.prototype.end 12 | ) 13 | 14 | exports.Test.prototype._end = function () { 15 | tests.remove(this) 16 | _end.apply(this, arguments) 17 | } 18 | 19 | exports.Test.prototype.run = function () { 20 | if (!this._cb || this._skip) { 21 | return this._end() 22 | } 23 | this.emit('prerun') 24 | try { 25 | tests.add(this) 26 | this._cb(this) 27 | } 28 | catch (err) { 29 | this.error(err) 30 | this._end() 31 | return 32 | } 33 | this.emit('run') 34 | } 35 | 36 | var createHarness = exports.createHarness 37 | exports.createHarness = function () { 38 | harness = createHarness.apply(this, arguments) 39 | return harness 40 | } 41 | 42 | process.browser ? 43 | global.onerror = killall : 44 | process.on('uncaughtException', killall) 45 | 46 | function killall (err) { 47 | if (tests.killall(err)) return 48 | 49 | // Died outside of a test block 50 | harness ? 51 | harness('uncaughtException', die) : 52 | tape('uncaughtException', die) 53 | 54 | function die (test) { 55 | test.error(err) 56 | test.end() 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /manager.js: -------------------------------------------------------------------------------- 1 | function Manager () { 2 | if (!(this instanceof Manager)) 3 | return new Manager 4 | 5 | this.tests = [] 6 | } 7 | 8 | Manager.prototype.add = function (test) { 9 | this.tests.push(test) 10 | } 11 | 12 | Manager.prototype.remove = function (test) { 13 | var tests = this.tests 14 | var l = tests.length 15 | 16 | while (l--) 17 | if (tests[l].name === test.name) 18 | tests.splice(l, 1) 19 | } 20 | 21 | 22 | Manager.prototype.killall = function (err) { 23 | var test 24 | var tests = this.tests.slice() 25 | if (!tests.length) return false 26 | 27 | var single = tests.length === 1 28 | while (test = tests.shift()) { 29 | if (single) test.error(err) 30 | test._end() 31 | } 32 | 33 | return true 34 | } 35 | 36 | module.exports = Manager 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tape-catch", 3 | "version": "1.0.6", 4 | "description": "a wrapper around tape that catches and reports exceptions", 5 | "main": "index.js", 6 | "scripts": { 7 | "copy-tape-tests": "bin/copy-tape-tests.js", 8 | "tape-tests": "npm run copy-tape-tests && tap test/*.js", 9 | "test:browser": "testling -u", 10 | "test:node": "tap test/catch-exceptions.js", 11 | "test": "npm run test:node" 12 | }, 13 | "testling": { 14 | "files": "./test/catch-exceptions/catch.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/michaelrhodes/tape-catch.git" 19 | }, 20 | "keywords": [ 21 | "tape", 22 | "exception", 23 | "catch" 24 | ], 25 | "author": "Michael Rhodes", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/michaelrhodes/tape-catch/issues" 29 | }, 30 | "homepage": "https://github.com/michaelrhodes/tape-catch", 31 | "dependencies": { 32 | "global": "~4.3.0" 33 | }, 34 | "peerDependencies": { 35 | "tape": "*" 36 | }, 37 | "devDependencies": { 38 | "concat-stream": "~1.4.7", 39 | "cpr": "~0.3.2", 40 | "falafel": "~0.3.1", 41 | "mkdirp": "~0.5.0", 42 | "run-series": "~1.0.2", 43 | "string.prototype.trim": "~1.1.2", 44 | "tap": "~0.5.0", 45 | "tape": "~4.5.1", 46 | "testling": "~1.7.1" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # tape-catch 2 | tape-catch is a wrapper around [tape](https://github.com/substack/tape) that reports uncaught errors in your tests. It re-adds functionality that was removed from tape in version 3.0.0. 3 | 4 | [![Build status](https://travis-ci.org/michaelrhodes/tape-catch.png?branch=master)](https://travis-ci.org/michaelrhodes/tape-catch) 5 | 6 | ## install 7 | ```sh 8 | $ npm install tape-catch 9 | ``` 10 | **note: tape is not installed alongside tape-catch.** 11 | 12 | tape-catch works with any and all versions of tape, so it leaves this choice to the user. 13 | 14 | ## example 15 | ```js 16 | var test = require('tape-catch') 17 | 18 | test('cause an exception', function (assert) { 19 | asdf 20 | }) 21 | 22 | test('still run this test', function (assert) { 23 | assert.equal(1 + 1, 2, 'this still ran') 24 | assert.end() 25 | }) 26 | ``` 27 | 28 | ``` 29 | TAP version 13 30 | # cause an exception 31 | not ok 1 ReferenceError: asdf is not defined 32 | --- 33 | operator: error 34 | expected: undefined 35 | actual: {} 36 | stack: 37 | ReferenceError: asdf is not defined 38 | at Test. (/path/to/example.js:4:3) 39 | at Test.bound [as _cb] (/path/to/node_modules/tape/lib/test.js:59:32) 40 | at Test.exports.Test.run (/path/to/index.js:17:10) 41 | at Test.bound [as run] (/path/to/node_modules/tape/lib/test.js:59:32) 42 | at Object.next [as _onImmediate] (/path/to/node_modules/tape/lib/results.js:66:15) 43 | at processImmediate [as _immediateCallback] (timers.js:345:15) 44 | ... 45 | # still run this test 46 | ok 2 this still ran 47 | 48 | 1..2 49 | # tests 2 50 | # pass 1 51 | # fail 1 52 | ``` 53 | 54 | ## license 55 | [MIT](http://opensource.org/licenses/MIT) 56 | -------------------------------------------------------------------------------- /test/catch-exceptions.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test; 2 | var concat = require('concat-stream'); 3 | var run = require('run-series'); 4 | var spawn = require('child_process').spawn; 5 | 6 | test('catch exceptions', function (t) { 7 | t.plan(10); 8 | 9 | var tests = []; 10 | ['catch', 'catch-harness.js'].forEach(function (file) { 11 | tests.push(function (next) { 12 | var ps = spawn(process.execPath, [ __dirname + '/catch-exceptions/' + file ]); 13 | ps.on('exit', function (code) { 14 | t.equal(code, 1); 15 | next(); 16 | }); 17 | ps.stdout.pipe(concat(function (body) { 18 | var results = body.toString(); 19 | if (/(Reference|Syntax)Error/.test(results)) { 20 | t.pass('reported exception'); 21 | } 22 | var tests = (/# tests\s+([0-9]+)/.exec(results) || [])[1]; 23 | var pass = (/# pass\s+([0-9]+)/.exec(results) || [])[1]; 24 | var fail = (/# fail\s+([0-9]+)/.exec(results) || [])[1]; 25 | t.equal(tests, '5'); 26 | t.equal(pass, '1'); 27 | t.equal(fail, '4'); 28 | })); 29 | }); 30 | }); 31 | run(tests); 32 | }); 33 | -------------------------------------------------------------------------------- /test/catch-exceptions/catch-harness.js: -------------------------------------------------------------------------------- 1 | var tape = require('../../'); 2 | 3 | var test = tape.createHarness(); 4 | 5 | test.createStream().on('end', function () { 6 | process.exit(test._exitCode); 7 | }).pipe(process.stdout); 8 | 9 | test('cause an exception', function (assert) { 10 | asdf; 11 | }); 12 | 13 | test('cause an exception within a callback', function (assert) { 14 | setTimeout(function () { 15 | async; 16 | }, 0); 17 | }); 18 | 19 | test('cause an exception within a nested callback', function (assert) { 20 | setTimeout(function () { 21 | setTimeout(function () { 22 | async_deep; 23 | }, 0); 24 | }, 0); 25 | }); 26 | 27 | test('still run this test', function (assert) { 28 | assert.equal(1 + 1, 2, 'this still ran'); 29 | assert.end(); 30 | }); 31 | 32 | eval('cause an exception outside of a tape block'); 33 | -------------------------------------------------------------------------------- /test/catch-exceptions/catch.js: -------------------------------------------------------------------------------- 1 | var tape = require('../../'); 2 | 3 | tape('cause an exception', function (assert) { 4 | asdf; 5 | }); 6 | 7 | tape('cause an exception within a callback', function (assert) { 8 | setTimeout(function () { 9 | async; 10 | }, 0); 11 | }); 12 | 13 | tape('cause an exception within a nested callback', function (assert) { 14 | setTimeout(function () { 15 | setTimeout(function () { 16 | async_deep; 17 | }, 0); 18 | }, 0); 19 | }); 20 | 21 | tape('still run this test', function (assert) { 22 | assert.equal(1 + 1, 2, 'this still ran'); 23 | assert.end(); 24 | }); 25 | 26 | eval('cause an exception outside of a tape block'); 27 | --------------------------------------------------------------------------------