├── .editorconfig ├── .gitattributes ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── errorHandling.js ├── fixtures ├── index.js └── test.js ├── passThrough.js ├── pipePatching.js ├── pipeUnpatching.js └── util └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [*.json] 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | temp/ 4 | coverage 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | temp/ 2 | coverage 3 | 4 | .editorconfig 5 | test 6 | .gitattributes 7 | .jshintrc 8 | .travis.yml 9 | gulpfile.js 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '5' 5 | - '4' 6 | - '0.12' 7 | - '0.10' 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Vsevolod Strukchinsky 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all 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 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :monkey: gulp-plumber 2 | [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][depstat-image]][depstat-url] 3 | 4 | > Prevent pipe breaking caused by errors from [gulp](https://github.com/wearefractal/gulp) plugins 5 | 6 | This :monkey:-patch plugin is fixing [issue with Node Streams piping](https://github.com/gulpjs/gulp/issues/91). For explanations, read [this small article](https://gist.github.com/floatdrop/8269868). 7 | 8 | Briefly it replaces `pipe` method and removes standard `onerror` handler on `error` event, which unpipes streams on error by default. 9 | 10 | ## Usage :monkey: 11 | 12 | First, install `gulp-plumber` as a development dependency: 13 | 14 | ```shell 15 | npm install --save-dev gulp-plumber 16 | ``` 17 | 18 | Then, add it to your `gulpfile.js`: 19 | 20 | ```javascript 21 | var plumber = require('gulp-plumber'); 22 | var coffee = require('gulp-coffee'); 23 | 24 | gulp.src('./src/*.ext') 25 | .pipe(plumber()) 26 | .pipe(coffee()) 27 | .pipe(gulp.dest('./dist')); 28 | ``` 29 | 30 | ## API :monkey: 31 | 32 | ### :monkey: plumber([options]) 33 | 34 | Returns Stream, that fixes `pipe` methods on Streams that are next in pipeline. 35 | 36 | #### options 37 | Type: `Object` / `Function` 38 | Default: `{}` 39 | 40 | Sets options described below from its properties. If type is `Function` it will be set as `errorHandler`. 41 | 42 | #### options.inherit 43 | Type: `Boolean` 44 | Default: `true` 45 | 46 | Monkeypatch `pipe` functions in underlying streams in pipeline. 47 | 48 | #### options.errorHandler 49 | Type: `Boolean` / `Function`
50 | Default: `true` 51 | 52 | Handle errors in underlying streams and output them to console. 53 | * `function` - it will be attached to stream `on('error')`. 54 | * `false` - error handler will not be attached. 55 | * `true` - default error handler will be attached. 56 | 57 | ### plumber.stop() 58 | 59 | This method will return default behaviour for pipeline after it was piped. 60 | 61 | ```javascript 62 | var plumber = require('gulp-plumber'); 63 | 64 | gulp.src('./src/*.scss') 65 | .pipe(plumber()) 66 | .pipe(sass()) 67 | .pipe(uglify()) 68 | .pipe(plumber.stop()) 69 | .pipe(gulp.dest('./dist')); 70 | ``` 71 | 72 | ## License :monkey: 73 | 74 | [MIT License](http://en.wikipedia.org/wiki/MIT_License) 75 | 76 | [npm-url]: https://npmjs.org/package/gulp-plumber 77 | [npm-image]: http://img.shields.io/npm/v/gulp-plumber.svg?style=flat 78 | 79 | [travis-url]: https://travis-ci.org/floatdrop/gulp-plumber 80 | [travis-image]: http://img.shields.io/travis/floatdrop/gulp-plumber.svg?style=flat 81 | 82 | [depstat-url]: https://david-dm.org/floatdrop/gulp-plumber 83 | [depstat-image]: http://img.shields.io/david/floatdrop/gulp-plumber.svg?style=flat 84 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var through2 = require('through2'); 4 | var EE = require('events').EventEmitter; 5 | var fancyLog = require('fancy-log'); 6 | var chalk = require('chalk'); 7 | var PluginError = require('plugin-error'); 8 | 9 | function removeDefaultHandler(stream, event) { 10 | var found = false; 11 | stream.listeners(event).forEach(function (item) { 12 | if (item.name === 'on' + event) { 13 | found = item; 14 | this.removeListener(event, item); 15 | } 16 | }, stream); 17 | return found; 18 | } 19 | 20 | function wrapPanicOnErrorHandler(stream) { 21 | var oldHandler = removeDefaultHandler(stream, 'error'); 22 | if (oldHandler) { 23 | stream.on('error', function onerror2(er) { 24 | if (EE.listenerCount(stream, 'error') === 1) { 25 | this.removeListener('error', onerror2); 26 | oldHandler.call(stream, er); 27 | } 28 | }); 29 | } 30 | } 31 | 32 | function defaultErrorHandler(error) { 33 | // onerror2 and this handler 34 | if (EE.listenerCount(this, 'error') < 3) { 35 | fancyLog( 36 | chalk.cyan('Plumber') + chalk.red(' found unhandled error:\n'), 37 | error.toString() 38 | ); 39 | } 40 | } 41 | 42 | function plumber(opts) { 43 | opts = opts || {}; 44 | 45 | if (typeof opts === 'function') { 46 | opts = {errorHandler: opts}; 47 | } 48 | 49 | var through = through2.obj(); 50 | through._plumber = true; 51 | 52 | if (opts.errorHandler !== false) { 53 | through.errorHandler = (typeof opts.errorHandler === 'function') ? 54 | opts.errorHandler : 55 | defaultErrorHandler; 56 | } 57 | 58 | function patchPipe(stream) { 59 | if (stream.pipe2) { 60 | wrapPanicOnErrorHandler(stream); 61 | stream._pipe = stream._pipe || stream.pipe; 62 | stream.pipe = stream.pipe2; 63 | stream._plumbed = true; 64 | } 65 | } 66 | 67 | through.pipe2 = function pipe2(dest) { 68 | if (!dest) { 69 | throw new PluginError('plumber', 'Can\'t pipe to undefined'); 70 | } 71 | 72 | this._pipe.apply(this, arguments); 73 | 74 | if (dest._unplumbed) { 75 | return dest; 76 | } 77 | 78 | removeDefaultHandler(this, 'error'); 79 | 80 | if (dest._plumber) { 81 | return dest; 82 | } 83 | 84 | dest.pipe2 = pipe2; 85 | 86 | // Patching pipe method 87 | if (opts.inherit !== false) { 88 | patchPipe(dest); 89 | } 90 | 91 | // Placing custom on error handler 92 | if (this.errorHandler) { 93 | dest.errorHandler = this.errorHandler; 94 | dest.on('error', this.errorHandler.bind(dest)); 95 | } 96 | 97 | dest._plumbed = true; 98 | 99 | return dest; 100 | }; 101 | 102 | patchPipe(through); 103 | 104 | return through; 105 | } 106 | 107 | module.exports = plumber; 108 | 109 | module.exports.stop = function () { 110 | var through = through2.obj(); 111 | through._unplumbed = true; 112 | return through; 113 | }; 114 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-plumber", 3 | "version": "1.2.1", 4 | "description": "Prevent pipe breaking caused by errors from gulp plugins", 5 | "keywords": [ 6 | "gulpplugin" 7 | ], 8 | "homepage": "https://github.com/floatdrop/gulp-plumber", 9 | "bugs": "https://github.com/floatdrop/gulp-plumber/issues", 10 | "author": { 11 | "name": "Vsevolod Strukchinsky", 12 | "email": "floatdrop@gmail.com", 13 | "url": "https://github.com/floatdrop" 14 | }, 15 | "main": "./index.js", 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/floatdrop/gulp-plumber.git" 19 | }, 20 | "scripts": { 21 | "test": "xo && mocha -R spec" 22 | }, 23 | "dependencies": { 24 | "chalk": "^1.1.3", 25 | "fancy-log": "^1.3.2", 26 | "plugin-error": "^0.1.2", 27 | "through2": "^2.0.3" 28 | }, 29 | "devDependencies": { 30 | "coveralls": "^2.11.6", 31 | "event-stream": "3.3.4", 32 | "gulp": "^3.9.1", 33 | "istanbul": "^0.4.2", 34 | "mocha": "^2.4.5", 35 | "mocha-lcov-reporter": "^1.0.0", 36 | "should": "^8.2.2", 37 | "through": "^2.3.8", 38 | "xo": "^0.12.1" 39 | }, 40 | "engines": { 41 | "node": ">=0.10", 42 | "npm": ">=1.2.10" 43 | }, 44 | "xo": { 45 | "ignore": [ 46 | "test/**" 47 | ] 48 | }, 49 | "license": "MIT" 50 | } 51 | -------------------------------------------------------------------------------- /test/errorHandling.js: -------------------------------------------------------------------------------- 1 | /*global describe, it, before, beforeEach */ 2 | 'use strict'; 3 | 4 | var should = require('should'), 5 | es = require('event-stream'), 6 | through2 = require('through2'), 7 | EE = require('events').EventEmitter, 8 | gulp = require('gulp'); 9 | 10 | var plumber = require('../'); 11 | 12 | var errorMessage = 'Bang!'; 13 | var fixturesGlob = ['./test/fixtures/index.js', './test/fixtures/test.js']; 14 | var delay = 20; 15 | 16 | describe('errorHandler', function () { 17 | 18 | beforeEach(function () { 19 | this.failingEmitStream = new es.through(function (file) { 20 | this.emit('data', file); 21 | this.emit('error', new Error('Bang!')); 22 | }); 23 | var i = 0; 24 | this.failingQueueStream = new es.through(function (file) { 25 | this.queue(file); 26 | i ++; 27 | if (i === 2) { 28 | this.emit('error', new Error('Bang!')); 29 | } 30 | }); 31 | }); 32 | 33 | before(function (done) { 34 | gulp.src(fixturesGlob) 35 | .pipe(es.writeArray(function (err, array) { 36 | this.expected = array; 37 | done(); 38 | }.bind(this))); 39 | }); 40 | 41 | it('should attach custom error handler', function (done) { 42 | gulp.src(fixturesGlob) 43 | .pipe(plumber({ errorHandler: function (error) { 44 | error.toString().should.containEql(errorMessage); 45 | done(); 46 | }})) 47 | .pipe(this.failingQueueStream); 48 | }); 49 | 50 | it('should attach custom error handler with function argument', function (done) { 51 | gulp.src(fixturesGlob) 52 | .pipe(plumber(function (error) { 53 | error.toString().should.containEql(errorMessage); 54 | done(); 55 | })) 56 | .pipe(this.failingQueueStream); 57 | }); 58 | 59 | it('should attach default error handler', function (done) { 60 | var mario = plumber(); 61 | mario.errorHandler = function (error) { 62 | error.toString().should.containEql(errorMessage); 63 | done(); 64 | }; 65 | gulp.src(fixturesGlob) 66 | .pipe(mario) 67 | .pipe(this.failingQueueStream); 68 | }); 69 | 70 | xit('default error handler should work', function (done) { 71 | // TODO: Find alternative way to test error handler (`gutil.log` was replaced by `fancyLog`) 72 | // var mario = plumber(); 73 | // var _ = gutil.log; 74 | // gutil.log = done.bind(null, null); 75 | // gulp.src(fixturesGlob) 76 | // .pipe(mario) 77 | // .pipe(this.failingQueueStream) 78 | // .on('end', function () { 79 | // gutil.log = _; 80 | // }); 81 | }); 82 | 83 | describe('should attach error handler', function () { 84 | it('in non-flowing mode', function (done) { 85 | var delayed = through2.obj(); 86 | setTimeout(delayed.write.bind(delayed, 'data'), delay); 87 | setTimeout(delayed.write.bind(delayed, 'data'), delay); 88 | delayed 89 | .pipe(plumber({ errorHandler: done.bind(null, null) })) 90 | .pipe(this.failingQueueStream); 91 | }); 92 | 93 | // it.only('in flowing mode', function (done) { 94 | // var delayed = through2.obj(); 95 | // setTimeout(delayed.write.bind(delayed, 'data'), delay); 96 | // setTimeout(delayed.write.bind(delayed, 'data'), delay); 97 | // delayed 98 | // .pipe(plumber({ errorHandler: done.bind(null, null) })) 99 | // // You cant do on('data') and pipe simultaniously. 100 | // .on('data', function () { }) 101 | // .pipe(this.failingQueueStream); 102 | // }); 103 | }); 104 | 105 | describe('should not attach error handler', function () { 106 | it('in non-flowing mode', function (done) { 107 | (function () { 108 | gulp.src(fixturesGlob) 109 | .pipe(plumber({ errorHandler: false })) 110 | .pipe(this.failingQueueStream) 111 | .on('end', done); 112 | }).should.throw(); 113 | done(); 114 | }); 115 | 116 | // it('in flowing mode', function (done) { 117 | // (function () { 118 | // gulp.src(fixturesGlob) 119 | // .pipe(plumber({ errorHandler: false })) 120 | // // You cant do on('data') and pipe simultaniously. 121 | // .on('data', function () { }) 122 | // .pipe(this.failingQueueStream) 123 | // .on('end', done); 124 | // }).should.throw(); 125 | // done(); 126 | // }); 127 | }); 128 | 129 | describe('throw', function () { 130 | it('on piping to undefined', function () { 131 | plumber().pipe.should.throw(/Can't pipe to undefined/); 132 | }); 133 | 134 | it('after cleanup', function (done) { 135 | var mario = plumber({ errorHandler: false }); 136 | var stream = mario.pipe(through2.obj()); 137 | 138 | (function () { 139 | stream.emit('error', new Error(errorMessage)); 140 | }).should.throw(); 141 | 142 | EE.listenerCount(mario, 'data').should.eql(0); 143 | EE.listenerCount(mario, 'drain').should.eql(0); 144 | EE.listenerCount(mario, 'error').should.eql(0); 145 | EE.listenerCount(mario, 'close').should.eql(0); 146 | 147 | EE.listenerCount(stream, 'data').should.eql(0); 148 | EE.listenerCount(stream, 'drain').should.eql(0); 149 | EE.listenerCount(stream, 'error').should.eql(0); 150 | EE.listenerCount(stream, 'close').should.eql(0); 151 | 152 | done(); 153 | }); 154 | 155 | }); 156 | 157 | }); 158 | -------------------------------------------------------------------------------- /test/fixtures/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/floatdrop/gulp-plumber/32afd5e09fb8e9d5f9420f57a4deb9be27084625/test/fixtures/index.js -------------------------------------------------------------------------------- /test/fixtures/test.js: -------------------------------------------------------------------------------- 1 | test.js -------------------------------------------------------------------------------- /test/passThrough.js: -------------------------------------------------------------------------------- 1 | /*global describe, it, before */ 2 | 'use strict'; 3 | 4 | var should = require('should'), 5 | through2 = require('through2'), 6 | es = require('event-stream'), 7 | gulp = require('gulp'); 8 | 9 | var plumber = require('../'); 10 | var fixturesGlob = ['./test/fixtures/*']; 11 | 12 | describe('stream', function () { 13 | 14 | it('piping into second plumber should keep piping', function (done) { 15 | gulp.src(fixturesGlob) 16 | .pipe(plumber()) 17 | .pipe(through2.obj()) 18 | .pipe(plumber()) 19 | .pipe(es.writeArray(function (err, array) { 20 | array.should.eql(this.expected); 21 | done(); 22 | }.bind(this))) 23 | .on('end', function () { 24 | done(); 25 | }); 26 | }); 27 | 28 | it('should work with es.readarray', function (done) { 29 | var expected = ['1\n', '2\n', '3\n', '4\n', '5\n']; 30 | 31 | es.readArray([1, 2, 3, 4, 5]) 32 | .pipe(plumber()) 33 | .pipe(es.stringify()) 34 | .pipe(es.writeArray(function (error, array) { 35 | array.should.eql(expected); 36 | done(); 37 | })); 38 | }); 39 | 40 | it('should emit `end` after source emit `finish`', function (done) { 41 | gulp.src(fixturesGlob) 42 | .pipe(plumber()) 43 | // Fetchout data 44 | .on('data', function () { }) 45 | .on('end', done) 46 | .on('error', done); 47 | }); 48 | 49 | describe('should passThrough all incoming files', function () { 50 | it('in non-flowing mode', function (done) { 51 | gulp.src(fixturesGlob) 52 | .pipe(plumber({ errorHandler: done })) 53 | .pipe(es.writeArray(function (err, array) { 54 | array.should.eql(this.expected); 55 | done(); 56 | }.bind(this))) 57 | .on('error', done); 58 | }); 59 | 60 | // it('in flowing mode', function (done) { 61 | // gulp.src(fixturesGlob) 62 | // .pipe(plumber({ errorHandler: done })) 63 | // // You cant do on('data') and pipe simultaniously. 64 | // .on('data', function (file) { should.exist(file); }) 65 | // .pipe(es.writeArray(function (err, array) { 66 | // array.should.eql(this.expected); 67 | // done(); 68 | // }.bind(this))) 69 | // .on('error', done); 70 | // }); 71 | }); 72 | 73 | before(function (done) { 74 | gulp.src(fixturesGlob) 75 | .pipe(es.writeArray(function (err, array) { 76 | this.expected = array; 77 | done(); 78 | }.bind(this))); 79 | }); 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /test/pipePatching.js: -------------------------------------------------------------------------------- 1 | /*global describe, it, before */ 2 | 'use strict'; 3 | 4 | var should = require('should'), 5 | es = require('event-stream'), 6 | noop = require('./util').noop, 7 | gulp = require('gulp'); 8 | 9 | var plumber = require('../'); 10 | var fixturesGlob = ['./test/fixtures/*']; 11 | 12 | describe('pipe', function () { 13 | 14 | it('should keep piping after error', function (done) { 15 | var expected = [1, 3, 5]; 16 | 17 | var badBoy = es.through(function (data) { 18 | if (data % 2 === 0) { 19 | return this.emit('error', new Error(data)); 20 | } 21 | this.emit('data', data); 22 | }); 23 | 24 | var actual = []; 25 | 26 | es.readArray([1, 2, 3, 4, 5, 6]) 27 | .pipe(plumber()) 28 | .pipe(badBoy) 29 | .pipe(es.through(function (data) { 30 | actual.push(data); 31 | this.emit('data', data); 32 | })) 33 | .on('error', function (err) { done(err); }) 34 | .on('end', function () { 35 | actual.should.eql(expected); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should skip patching with `inherit` === false', function (done) { 41 | var lastNoop = noop(); 42 | var mario = plumber({ inherit: false }); 43 | gulp.src(fixturesGlob) 44 | .pipe(mario) 45 | .pipe(noop()) 46 | .pipe(noop()) 47 | .pipe(lastNoop) 48 | .on('end', function () { 49 | should.not.exist(lastNoop._plumbed); 50 | done(); 51 | }); 52 | }); 53 | 54 | describe('should be patched at itself and underlying streams', function () { 55 | it('in non-flowing mode', function (done) { 56 | var lastNoop = noop(); 57 | var mario = plumber(); 58 | var m = gulp.src(fixturesGlob) 59 | .pipe(mario) 60 | .pipe(noop()) 61 | .pipe(noop()) 62 | .pipe(lastNoop) 63 | .on('end', function () { 64 | should.exist(lastNoop._plumbed); 65 | done(); 66 | }); 67 | }); 68 | 69 | it('in flowing mode', function (done) { 70 | var lastNoop = noop(); 71 | var mario = plumber(); 72 | gulp.src(fixturesGlob) 73 | .pipe(mario) 74 | .on('data', function (file) { should.exist(file); }) 75 | .pipe(noop()) 76 | .pipe(noop()) 77 | .pipe(lastNoop) 78 | .on('end', function () { 79 | should.exist(lastNoop._plumbed); 80 | done(); 81 | }); 82 | }); 83 | }); 84 | 85 | it('piping into second plumber should does nothing', function (done) { 86 | var lastNoop = noop(); 87 | gulp.src(fixturesGlob) 88 | .pipe(plumber()) 89 | .pipe(noop()).pipe(noop()) 90 | .pipe(plumber()) 91 | .pipe(lastNoop) 92 | .on('end', function () { 93 | should.exist(lastNoop._plumbed); 94 | done(); 95 | }); 96 | }); 97 | 98 | before(function (done) { 99 | gulp.src(fixturesGlob) 100 | .pipe(es.writeArray(function (err, array) { 101 | this.expected = array; 102 | done(); 103 | }.bind(this))); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /test/pipeUnpatching.js: -------------------------------------------------------------------------------- 1 | /*global describe, it, before */ 2 | 'use strict'; 3 | 4 | var es = require('event-stream'), 5 | gulp = require('gulp'); 6 | 7 | var plumber = require('../'); 8 | var fixturesGlob = ['./test/fixtures/*']; 9 | 10 | describe('unpipe', function () { 11 | 12 | it('should not keep piping after error', function (done) { 13 | var expected = [1, 3, 5]; 14 | 15 | var badBoy = es.through(function (data) { 16 | if (data % 2 === 0) { 17 | return this.emit('error', new Error(data)); 18 | } 19 | this.emit('data', data); 20 | }); 21 | 22 | var badass = es.through(function (data) { 23 | if (data === 5) { 24 | return this.emit('error', new Error('Badass')); 25 | } 26 | this.emit('data', data); 27 | }); 28 | 29 | var actual = []; 30 | 31 | es.readArray([1, 2, 3, 4, 5, 6]) 32 | .pipe(plumber()) 33 | .pipe(badBoy) 34 | .pipe(es.through(function (data) { 35 | actual.push(data); 36 | this.emit('data', data); 37 | })) 38 | .pipe(plumber.stop()) 39 | .pipe(badass) 40 | .on('error', function (err) { 41 | err.should.eql(new Error('Badass')); 42 | actual.should.eql(expected); 43 | done(); 44 | }) 45 | .on('end', function () { 46 | done('Error was not fired'); 47 | }); 48 | }); 49 | 50 | before(function (done) { 51 | gulp.src(fixturesGlob) 52 | .pipe(es.writeArray(function (err, array) { 53 | this.expected = array; 54 | done(); 55 | }.bind(this))); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/util/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var through = require('through'); 4 | 5 | module.exports.noop = function () { 6 | return through(function (data) { 7 | this.queue(data); 8 | }); 9 | }; 10 | --------------------------------------------------------------------------------