├── .gitignore ├── .travis.yml ├── test.js ├── .editorconfig ├── webpack.test.js ├── package.json ├── index.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | output 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | - "6" 5 | - "7" 6 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var test = require('tape') 2 | 3 | test('successful test', function (t) { 4 | t.equal(1, 1) 5 | t.end() 6 | }) 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /webpack.test.js: -------------------------------------------------------------------------------- 1 | var TapWebpackPlugin = require('./') 2 | 3 | module.exports = { 4 | target: 'node', 5 | entry: ['./test'], 6 | 7 | output: { 8 | path: 'output', 9 | filename: 'test.js' 10 | }, 11 | 12 | plugins: [ 13 | new TapWebpackPlugin(), 14 | new TapWebpackPlugin({ reporter: 'tap-spec' }) 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tap-webpack-plugin", 3 | "version": "2.0.0", 4 | "description": "Run TAP tests on every compile with webpack", 5 | "main": "index.js", 6 | "scripts": { 7 | "pretest": "standard", 8 | "test": "webpack --config webpack.test.js" 9 | }, 10 | "keywords": [ 11 | "test", 12 | "webpack", 13 | "tap" 14 | ], 15 | "author": "Conrad Zimmerman ", 16 | "license": "ISC", 17 | "dependencies": { 18 | "execspawn": "^1.0.1", 19 | "run-parallel": "^1.1.6", 20 | "tap-out": "^1.4.2" 21 | }, 22 | "devDependencies": { 23 | "standard": "^8.6.0", 24 | "tap-spec": "^4.1.1", 25 | "tape": "^4.6.3", 26 | "webpack": "^2.2.1" 27 | }, 28 | "engines": { 29 | "node": ">=4" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var tapOut = require('tap-out') 2 | var runParallel = require('run-parallel') 3 | var execSpawn = require('execspawn') 4 | var spawn = require('child_process').spawn 5 | 6 | function TapWebpackPlugin (options) { 7 | this.options = options || {} 8 | } 9 | 10 | TapWebpackPlugin.prototype.apply = function (compiler) { 11 | compiler.plugin('after-emit', run.bind(null, this.options)) 12 | } 13 | 14 | function run (options, compilation, callback) { 15 | var entry = compilation.chunks.filter((c) => c.hasRuntime()) 16 | var files = entry.map((c) => c.files[0]) 17 | var assets = files.map((f) => compilation.assets[f]) 18 | var source = assets.map((a) => a.source()).join('\n') 19 | 20 | var proc = spawn(process.execPath, { stdio: ['pipe', 'pipe', 'inherit'] }) 21 | proc.stdin.end(source, 'utf8') 22 | return runParallel([ 23 | options.reporter ? report : parse, 24 | exit 25 | ], callback) 26 | 27 | function report (callback) { 28 | var reporter = execSpawn(options.reporter, 29 | { stdio: ['pipe', 'inherit', 'inherit'] }) 30 | proc.stdout.pipe(reporter.stdin) 31 | reporter.on('exit', exited) 32 | 33 | function exited (code) { 34 | if (code !== 0) addError('test reporter non-zero exit code') 35 | return callback() 36 | } 37 | } 38 | 39 | function parse (callback) { 40 | proc.stdout.pipe(tapOut(parsed)) 41 | 42 | function parsed (err, results) { 43 | if (err) { 44 | addError('could not parse TAP output') 45 | } else if (results.fail.length > 0) { 46 | results.fail.map(getError).forEach(addError) 47 | } 48 | return callback() 49 | 50 | function getError (f) { 51 | return getMessage(results.tests[f.test - 1], f) 52 | } 53 | } 54 | } 55 | 56 | function exit (callback) { 57 | proc.on('exit', exited) 58 | 59 | function exited (code) { 60 | if (code !== 0) addError('tests failed') 61 | return callback() 62 | } 63 | } 64 | 65 | function addError (message) { 66 | compilation.errors.push(new Error(message)) 67 | } 68 | } 69 | 70 | function getMessage (test, fail) { 71 | var name = test ? test.name + ' - ' + fail.name : fail.name 72 | var message = 'failed test: ' + name 73 | 74 | var error = fail.error 75 | if (error && error.expected && error.actual) { 76 | message += '\n Expected: ' + error.expected + 77 | '\n Actual: ' + error.actual 78 | } 79 | 80 | return message 81 | } 82 | 83 | module.exports = TapWebpackPlugin 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tap-webpack-plugin 2 | 3 | [![travis][travis-image]][travis-url] 4 | [![js-standard-style][standard-image]][standard-url] 5 | [![npm][npm-image]][npm-url] 6 | 7 | Run TAP tests on every compile with webpack 8 | 9 | This is a plugin for [webpack][webpack-url] that runs the generated output 10 | bundle with Node.js and parses the output as [TAP][tap-url]. This works well 11 | with `webpack --watch` as it will run your tests every time a file changes. 12 | 13 | Note: You should use the `target: 'node'` option in your webpack configuration 14 | as the generated output will be run with Node.js. 15 | 16 | ## Using a TAP Reporter 17 | 18 | By default this plugin will parse the TAP output and report any errors from the 19 | TAP output to webpack. If instead you want to pass the TAP output to a 20 | [TAP reporter][reporters], just use the `reporter` option, like this: 21 | 22 | ```js 23 | new TapWebpackPlugin({ reporter: 'tap-spec' }) 24 | ``` 25 | 26 | The `reporter` option specifies the command line for the reporter. It will be 27 | run in a shell, so you can pass arguments such as `'tap-spec --no-color'`. 28 | Output from the TAP reporter will be written directly to the stdout of the 29 | webpack process. If the TAP reporter command exits with a non-zero exit code, 30 | the plugin will report an error to webpack. 31 | 32 | ## Example 33 | 34 | ```js 35 | // Your webpack.config.js 36 | var TapWebpackPlugin = require('tap-webpack-plugin') 37 | 38 | module.exports = { 39 | target: 'node', 40 | 41 | entry: ['./test'], 42 | 43 | output: { 44 | path: 'output', 45 | filename: 'test.js' 46 | }, 47 | 48 | plugins: [ 49 | new TapWebpackPlugin(), 50 | 51 | // or with a reporter: 52 | new TapWebpackPlugin({ reporter: 'tap-spec' }) 53 | ] 54 | } 55 | ``` 56 | 57 | ```js 58 | // test.js 59 | var test = require('tape') 60 | 61 | test('successful test', function (t) { 62 | t.equal(1, 1) 63 | t.end() 64 | }) 65 | ``` 66 | 67 | ## Example App 68 | 69 | You can also generate your main application bundle and a test bundle by 70 | exporting an array of configuration objects in the `webpack.config.js`: 71 | 72 | ```js 73 | // Your webpack.config.js 74 | var TapWebpackPlugin = require('tap-webpack-plugin') 75 | 76 | module.exports = [ 77 | // main bundle configuration 78 | { 79 | entry: ['./main'], 80 | output: { 81 | path: 'output/dist', 82 | filename: 'main.js' 83 | } 84 | }, 85 | 86 | // test bundle configuration 87 | { 88 | target: 'node', 89 | 90 | entry: ['./test'], 91 | 92 | output: { 93 | path: 'output', 94 | filename: 'test.js' 95 | }, 96 | 97 | plugins: [ 98 | new TapWebpackPlugin() 99 | ] 100 | } 101 | ]; 102 | ``` 103 | 104 | Then run `webpack` just like normal and your main bundle will be generated and 105 | your tests will be compiled and run, all in one command! 106 | 107 | [travis-image]: https://img.shields.io/travis/conradz/tap-webpack-plugin.svg?style=flat 108 | [travis-url]: https://travis-ci.org/conradz/tap-webpack-plugin 109 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat 110 | [standard-url]: https://github.com/feross/standard 111 | [npm-image]: https://img.shields.io/npm/v/tap-webpack-plugin.svg?style=flat 112 | [npm-url]: https://npmjs.org/package/tap-webpack-plugin 113 | [webpack-url]: https://webpack.github.io/ 114 | [tap-url]: https://testanything.org/ 115 | [reporters]: https://github.com/sindresorhus/awesome-tap#reporters 116 | --------------------------------------------------------------------------------