4 var test = debug ? function () {} : require('tap').test
5 var test_ = !debug ? function () {} : require('tap').test
6 , path = require('path')
8 , util = require('util')
9 , TransformStream = require('readable-stream').Transform
10 , through = require('through2')
11 , proxyquire = require('proxyquire')
12 , streamapi = require('../stream-api')
13 , readdirp = require('..')
14 , root = path.join(__dirname, 'bed')
22 // see test/readdirp.js for test bed layout
24 function opts (extend) {
25 var o = { root: root };
28 for (var prop in extend) {
29 o[prop] = extend[prop];
36 var result = { entries: [], errors: [], ended: false }
37 , dst = new TransformStream({ objectMode: true });
39 dst._transform = function (entry, _, cb) {
40 result.entries.push(entry);
44 dst._flush = function (cb) {
53 test('\nintegrated', function (t) {
54 t.test('\n# reading root without filter', function (t) {
58 .on('error', function (err) {
59 t.fail('should not throw error', err);
63 function (result, _ , cb) {
64 t.equals(result.entries.length, totalFiles, 'emits all files');
65 t.ok(result.ended, 'ends stream');
72 t.test('\n# normal: ["*.ext1", "*.ext3"]', function (t) {
75 readdirp(opts( { fileFilter: [ '*.ext1', '*.ext3' ] } ))
76 .on('error', function (err) {
77 t.fail('should not throw error', err);
81 function (result, _ , cb) {
82 t.equals(result.entries.length, ext1Files + ext3Files, 'all ext1 and ext3 files');
83 t.ok(result.ended, 'ends stream');
90 t.test('\n# files only', function (t) {
93 readdirp(opts( { entryType: 'files' } ))
94 .on('error', function (err) {
95 t.fail('should not throw error', err);
99 function (result, _ , cb) {
100 t.equals(result.entries.length, totalFiles, 'returned files');
101 t.ok(result.ended, 'ends stream');
108 t.test('\n# directories only', function (t) {
111 readdirp(opts( { entryType: 'directories' } ))
112 .on('error', function (err) {
113 t.fail('should not throw error', err);
117 function (result, _ , cb) {
118 t.equals(result.entries.length, totalDirs, 'returned directories');
119 t.ok(result.ended, 'ends stream');
126 t.test('\n# both directories + files', function (t) {
129 readdirp(opts( { entryType: 'both' } ))
130 .on('error', function (err) {
131 t.fail('should not throw error', err);
135 function (result, _ , cb) {
136 t.equals(result.entries.length, totalDirs + totalFiles, 'returned everything');
137 t.ok(result.ended, 'ends stream');
144 t.test('\n# directory filter with directories only', function (t) {
147 readdirp(opts( { entryType: 'directories', directoryFilter: [ 'root_dir1', '*dir1_subdir1' ] } ))
148 .on('error', function (err) {
149 t.fail('should not throw error', err);
153 function (result, _ , cb) {
154 t.equals(result.entries.length, 2, 'two directories');
155 t.ok(result.ended, 'ends stream');
162 t.test('\n# directory and file filters with both entries', function (t) {
165 readdirp(opts( { entryType: 'both', directoryFilter: [ 'root_dir1', '*dir1_subdir1' ], fileFilter: [ '!*.ext1' ] } ))
166 .on('error', function (err) {
167 t.fail('should not throw error', err);
171 function (result, _ , cb) {
172 t.equals(result.entries.length, 6, '2 directories and 4 files');
173 t.ok(result.ended, 'ends stream');
180 t.test('\n# negated: ["!*.ext1", "!*.ext3"]', function (t) {
183 readdirp(opts( { fileFilter: [ '!*.ext1', '!*.ext3' ] } ))
184 .on('error', function (err) {
185 t.fail('should not throw error', err);
189 function (result, _ , cb) {
190 t.equals(result.entries.length, totalFiles - ext1Files - ext3Files, 'all but ext1 and ext3 files');
191 t.ok(result.ended, 'ends stream');
197 t.test('\n# no options given', function (t) {
200 .on('error', function (err) {
201 t.similar(err.toString() , /Need to pass at least one argument/ , 'emits meaningful error');
206 t.test('\n# mixed: ["*.ext1", "!*.ext3"]', function (t) {
209 readdirp(opts( { fileFilter: [ '*.ext1', '!*.ext3' ] } ))
210 .on('error', function (err) {
211 t.similar(err.toString() , /Cannot mix negated with non negated glob filters/ , 'emits meaningful error');
218 test('\napi separately', function (t) {
220 t.test('\n# handleError', function (t) {
223 var api = streamapi()
224 , warning = new Error('some file caused problems');
227 .on('warn', function (err) {
228 t.equals(err, warning, 'warns with the handled error');
230 api.handleError(warning);
233 t.test('\n# when stream is paused and then resumed', function (t) {
235 var api = streamapi()
237 , fatalError = new Error('fatal!')
238 , nonfatalError = new Error('nonfatal!')
239 , processedData = 'some data'
243 .on('warn', function (err) {
244 t.equals(err, nonfatalError, 'emits the buffered warning');
245 t.ok(resumed, 'emits warning only after it was resumed');
247 .on('error', function (err) {
248 t.equals(err, fatalError, 'emits the buffered fatal error');
249 t.ok(resumed, 'emits errors only after it was resumed');
251 .on('data', function (data) {
252 t.equals(data, processedData, 'emits the buffered data');
253 t.ok(resumed, 'emits data only after it was resumed');
257 api.processEntry(processedData);
258 api.handleError(nonfatalError);
259 api.handleFatalError(fatalError);
261 setTimeout(function () {
267 t.test('\n# when a stream is paused it stops walking the fs', function (t) {
269 mockedAPI = streamapi();
271 mockedAPI.processEntry = function (entry) {
272 if (!resumed) t.notOk(true, 'should not emit while paused')
273 t.ok(entry, 'emitted while resumed')
274 }.bind(mockedAPI.stream)
276 function wrapper () {
280 var readdirp = proxyquire('../readdirp', {'./stream-api': wrapper})
281 , stream = readdirp(opts())
282 .on('error', function (err) {
283 t.fail('should not throw error', err);
285 .on('end', function () {
290 setTimeout(function () {
296 t.test('\n# when a stream is destroyed, it emits "closed", but no longer emits "data", "warn" and "error"', function (t) {
297 var api = streamapi()
298 , fatalError = new Error('fatal!')
299 , nonfatalError = new Error('nonfatal!')
300 , processedData = 'some data'
304 var stream = api.stream
305 .on('warn', function (err) {
306 t.ok(!stream._destroyed, 'emits warning until destroyed');
308 .on('error', function (err) {
309 t.ok(!stream._destroyed, 'emits errors until destroyed');
311 .on('data', function (data) {
312 t.ok(!stream._destroyed, 'emits data until destroyed');
314 .on('close', function () {
315 t.ok(stream._destroyed, 'emits close when stream is destroyed');
319 api.processEntry(processedData);
320 api.handleError(nonfatalError);
321 api.handleFatalError(fatalError);
323 setTimeout(function () {
326 t.notOk(stream.readable, 'stream is no longer readable after it is destroyed')
328 api.processEntry(processedData);
329 api.handleError(nonfatalError);
330 api.handleFatalError(fatalError);
332 process.nextTick(function () {
333 t.pass('emits no more data, warn or error events after it was destroyed')