├── .gitignore ├── .npmignore ├── LICENSE.md ├── README.md ├── demo └── index.html ├── index.js ├── lib └── is-tap-string.js ├── package.json ├── register.js ├── test ├── test-instrument-fail.js ├── test-instrument-pass.js └── test-plugin.js └── tool └── build.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | test 7 | test.js 8 | demo/ 9 | .npmignore 10 | LICENSE.md -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2015 Jam3 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tap-dev-tool 2 | 3 | [![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges) 4 | 5 | [(Click for a live demo)](https://jam3.github.io/tap-dev-tool/demo/) 6 | 7 | Prettify TAP output in Chrome/FireFox DevTools console. Works best with [tape](https://www.npmjs.com/package/tape). 8 | 9 | ![fail](http://i.imgur.com/xwYZUc7.png) 10 | 11 | Comes in the form of a Browserify plugin, so you don't need to change your source code during development. PRs for other integrations (Babel, Webpack) welcome. 12 | 13 | ## Install 14 | 15 | You can install with [npm](https://www.npmjs.com/): 16 | 17 | ```sh 18 | npm install tap-dev-tool --save-dev 19 | ``` 20 | 21 | ## Usage 22 | 23 | [![NPM](https://nodei.co/npm/tap-dev-tool.png)](https://www.npmjs.com/package/tap-dev-tool) 24 | 25 | ### browserify 26 | 27 | The preferred way of using this is with a plugin during development. After installing, you can use it with browserify like this: 28 | 29 | ```sh 30 | browserify index.js --plugin tap-dev-tool > bundle.js 31 | ``` 32 | 33 | Works well during development with [watchify](https://github.com/substack/watchify) and [budo](https://github.com/mattdesl/budo). 34 | 35 | ### require hook 36 | 37 | If you don't want to use the plugin, you can require the tool somewhere at the start of your application. 38 | 39 | ```js 40 | require('tap-dev-tool/register') 41 | 42 | var test = require('tape') 43 | 44 | test('should do something', function (t) { 45 | ... 46 | }) 47 | ``` 48 | 49 | ## See Also 50 | 51 | - [budo](https://github.com/mattdesl/budo) - fast browser development 52 | - [hihat](https://github.com/Jam3/hihat) - launches DevTools in a new process 53 | - [tap-browser-el](https://www.npmjs.com/package/tap-browser-el) 54 | - [tap-browser-color](https://www.npmjs.com/package/tap-browser-color) 55 | - [tap-console-parser](https://www.npmjs.com/package/tap-console-parser) 56 | 57 | ## License 58 | 59 | MIT, see [LICENSE.md](http://github.com/Jam3/tap-dev-tool/blob/master/LICENSE.md) for details. 60 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | tap-dev-tool 7 | 8 | 9 | Open the console and reload the page! 10 | 11 | 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var through = require('through2') 2 | var fs = require('fs') 3 | var path = require('path') 4 | 5 | var build = path.join(__dirname, 'dist', 'build.js') 6 | var prelude = fs.readFileSync(build) 7 | 8 | module.exports = function tapDevTool (b) { 9 | var written = false 10 | var bundle = b.bundle.bind(b) 11 | b.bundle = function (cb) { 12 | written = false 13 | 14 | var output = through(write) 15 | var pipeline = bundle(cb) 16 | pipeline.pipe(output) 17 | pipeline.on('error', function (err) { 18 | output.emit('error', err) 19 | }) 20 | return output 21 | } 22 | 23 | function write (buf, enc, next) { 24 | if (!written) { 25 | this.push(prelude) 26 | written = true 27 | } 28 | this.push(buf) 29 | next() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/is-tap-string.js: -------------------------------------------------------------------------------- 1 | module.exports = function isTapString (str) { 2 | return /^\s*(not )?ok/.test(str) || 3 | /^TAP version [0-9]+/.test(str) || 4 | /^[0-9]+\.\.[0-9]+/.test(str) || 5 | /^\s+(\.\.\.)|(\-\-\-)$/.test(str) || 6 | /^\s*(actual|operator|expected|at)\:/.test(str) || 7 | /^\#\s/.test(str) 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tap-dev-tool", 3 | "version": "1.3.0", 4 | "description": "prettifies TAP in the browser's console", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Matt DesLauriers", 9 | "email": "dave.des@gmail.com", 10 | "url": "https://github.com/mattdesl" 11 | }, 12 | "dependencies": { 13 | "tap-console-parser": "^1.0.2", 14 | "through2": "^0.6.5" 15 | }, 16 | "devDependencies": { 17 | "browserify": "^10.2.3", 18 | "budo": "^4.0.0", 19 | "garnish": "^2.1.3", 20 | "standard": "^4.2.0", 21 | "tape": "^4.0.0", 22 | "uglify-js": "^2.4.23" 23 | }, 24 | "scripts": { 25 | "test": "standard", 26 | "uglify": "browserify register.js | uglifyjs > dist/build.js", 27 | "build": "npm run uglify && node tool/build.js", 28 | "build-demo": "browserify test/test-plugin.js -p ./ | uglifyjs -cm > demo/bundle.js", 29 | "prepublish": "npm run build", 30 | "start": "budo test/test-plugin.js:bundle.js --dir demo --live --verbose -p ./ | garnish" 31 | }, 32 | "standard": { 33 | "ignore": [ 34 | "./dist/*.js" 35 | ] 36 | }, 37 | "keywords": [ 38 | "tap", 39 | "browser", 40 | "visualize", 41 | "tape", 42 | "test", 43 | "tests", 44 | "console", 45 | "log", 46 | "pretty", 47 | "css", 48 | "color", 49 | "colors", 50 | "colour", 51 | "colours" 52 | ], 53 | "repository": { 54 | "type": "git", 55 | "url": "git://github.com/Jam3/tap-dev-tool.git" 56 | }, 57 | "homepage": "https://github.com/Jam3/tap-dev-tool", 58 | "bugs": { 59 | "url": "https://github.com/Jam3/tap-dev-tool/issues" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /register.js: -------------------------------------------------------------------------------- 1 | var fails = [] 2 | var tests = {} 3 | var start = null 4 | var isTAP = require('./lib/is-tap-string') 5 | var previousLog = '' 6 | 7 | var parser = require('tap-console-parser')() 8 | .on('complete', function (output) { 9 | var time = Date.now() - start 10 | parser.detach() 11 | console.log('\n') 12 | 13 | if (!output.ok) { 14 | console.log('%c\nFailing Tests: ' + output.fail, 'font-weight: bold') 15 | Object.keys(tests).forEach(function (key) { 16 | var test = tests[key] 17 | var asserts = fails[key] || [] 18 | if (asserts.length === 0) { 19 | return 20 | } 21 | 22 | console.log('%c ' + test.name, 'color: gray') 23 | 24 | asserts.forEach(function (fail) { 25 | console.log('%c ⨯ ' + fail.name, 'font-weight: bold; color: #c32b2b') 26 | 27 | console.log('%c ---', '') 28 | var str = fail.error.raw 29 | .split('\n') 30 | .map(function (x) { 31 | return ' ' + x 32 | }) 33 | .join('\n') 34 | 35 | console.log(str) 36 | console.log('%c ...', '') 37 | }) 38 | }) 39 | console.log('') 40 | } 41 | 42 | if (output.ok) { 43 | console.log('%c✓ ok', 'color: white; padding: 1px 10px; background: #8eca6c; font-weight: bold') 44 | } else { 45 | console.log('%c⨯ not ok', 'color: white; padding: 1px 10px; background: #c32b2b; font-weight: not ok') 46 | } 47 | if (!output.ok) { 48 | console.log('%c total: ' + output.total, 'font-weight: bold') 49 | } 50 | console.log('%c passing: ' + output.pass, 'color: #72b432; font-weight: bold') 51 | if (!output.ok) { 52 | console.log('%c failing: ' + output.fail, 'color: #c32b2b; font-weight: bold') 53 | } 54 | console.log('%c duration: ' + time + ' ms', 'color: gray') 55 | 56 | start = null 57 | }) 58 | .on('assert', function (assert) { 59 | if (assert.ok) { 60 | parser.log('%c ✓', 'color: #8eca6c', assert.name) 61 | } else { 62 | if (fails[assert.test]) { 63 | fails[assert.test].push(assert) 64 | } else { 65 | fails[assert.test] = [ assert ] 66 | } 67 | 68 | parser.log('%c ⨯ ' + assert.name, 'color: #c32b2b; font-weight: bold') 69 | } 70 | }) 71 | .on('test', function (test) { 72 | if (start === null) { 73 | start = Date.now() 74 | } else { 75 | parser.log('') 76 | } 77 | parser.log('%c' + test.name, 'color: gray') 78 | tests[test.number] = test 79 | }) 80 | .on('log', function (args) { 81 | // allow comments to print as per normal 82 | var str = args.join(' ') 83 | if (str && !isTAP(str)) { 84 | if (!previousLog || !/^\s*(expected|actual|at)\:\s*$/.test(previousLog)) { 85 | parser.log.apply(null, args) 86 | } 87 | } 88 | previousLog = str 89 | }) 90 | -------------------------------------------------------------------------------- /test/test-instrument-fail.js: -------------------------------------------------------------------------------- 1 | require('../register') 2 | 3 | var test = require('tape') 4 | 5 | console.log('This is a regular comment', { foo: 2 }) 6 | 7 | test('should test things', function (t) { 8 | t.plan(4) 9 | t.deepEqual([ 2, 3 ], [ 2, 3 ]) 10 | t.deepEqual(2, 5) 11 | t.equal('hello', 5) 12 | t.throws(function () { 13 | throw new Error('FOO') 14 | }, 'yep it throws') 15 | }) 16 | 17 | test('even more testing of things', function (t) { 18 | t.ok(false, 'this is not ok') 19 | t.end() 20 | }) 21 | -------------------------------------------------------------------------------- /test/test-instrument-pass.js: -------------------------------------------------------------------------------- 1 | require('../register') 2 | 3 | var test = require('tape') 4 | 5 | test('should test things', function (t) { 6 | t.plan(4) 7 | t.deepEqual([ 2, 3 ], [ 2, 3 ]) 8 | setTimeout(function () { 9 | t.deepEqual(5, 5) 10 | }, 1000) 11 | t.equal('hello', 'hello') 12 | t.throws(function () { 13 | throw new Error('FOO') 14 | }, 'yep it throws') 15 | }) 16 | 17 | test('even more testing of things', function (t) { 18 | t.ok(true, 'this is ok') 19 | t.end() 20 | }) 21 | -------------------------------------------------------------------------------- /test/test-plugin.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | test('should test things', function (t) { 4 | t.plan(4) 5 | t.deepEqual([ 2, 3 ], [ 2, 3 ]) 6 | setTimeout(function () { 7 | t.deepEqual(5, 5) 8 | }, 1000) 9 | t.equal('hello', 'hello') 10 | t.throws(function () { 11 | throw new Error('FOO') 12 | }, 'yep it throws') 13 | }) 14 | 15 | test('even more testing of things', function (t) { 16 | t.ok(true, 'this is ok') 17 | t.end() 18 | }) 19 | -------------------------------------------------------------------------------- /tool/build.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | 4 | var file = path.resolve(__dirname, '../', 'dist', 'build.js') 5 | fs.readFile(file, 'utf8', function (err, data) { 6 | if (err) throw err 7 | data = data.replace(/[\n\r]/g, '') 8 | fs.writeFile(file, data, function (err) { 9 | if (err) throw err 10 | }) 11 | }) 12 | --------------------------------------------------------------------------------