diff --git a/gulpfile.js b/gulpfile.js index adad2ab8f9..0f310bf8ec 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -85,7 +85,8 @@ var angularBuilder = { var treatTestErrorsAsFatal = true; function runJasmineTests(globs, done) { - fork('./tools/traceur-jasmine', globs, { + var args = ['--'].concat(globs); + fork('./tools/traceur-jasmine', args, { stdio: 'inherit' }).on('close', function jasmineCloseHandler(exitCode) { if (exitCode && treatTestErrorsAsFatal) { @@ -466,7 +467,7 @@ gulp.task('test.unit.cjs', ['build/clean.js', 'build.tools'], function (neverDon gulp.task('test.unit.tools/ci', function(done) { - runJasmineTests(['dist/tools/**/*.spec.js'], done); + runJasmineTests(['dist/tools/**/*.spec.js', 'tools/**/*.spec.js'], done); }); diff --git a/tools/build/watch.spec.js b/tools/build/watch.spec.js new file mode 100644 index 0000000000..b1f06dce6b --- /dev/null +++ b/tools/build/watch.spec.js @@ -0,0 +1,154 @@ +var rewire = require('rewire'); + +describe('watch()', function() { + var timeout; + var watch; + var watcher; + var unmock; + + beforeEach(function() { + timeout = mockTimeout(); + var watchModule = rewire('./watch'); + unmock = watchModule.__set__(timeout.mocks); + watch = watchModule.__get__('watch'); + }); + + afterEach(function() { + // Shouldn't really be necessary, but... + if (watcher) { + watcher.close(); + watcher = null; + } + unmock(); + expect(timeout.pending).toBe(0); + timeout = null; + }); + + + it('should fire callback once for events which occur within `delay` window', function() { + var cb = jasmine.createSpy('callback'); + watcher = watch('./$$fake_path/**/*', { delay: 10 }, cb); + + watcher._emit('add', './$$fake_path/test.txt'); + timeout.flush(9); + expect(cb).not.toHaveBeenCalled(); + + watcher._emit('change', './$$fake_path/test.txt'); + watcher._emit('add', './$$fake_path/test2.txt'); + watcher._emit('change', './$$fake_path/test2.txt'); + watcher._emit('add', './$$fake_path/test3.txt'); + watcher._emit('change', './$$fake_path/test3.txt'); + expect(cb).not.toHaveBeenCalled(); + + timeout.flush(1); + expect(cb.calls.count()).toBe(1); + }); + + + it('should trigger callback if events are collected during task running', function() { + var calls = 0; + function cb(done) { + if (++calls !== 1) return done(); + + watcher._emit('change', './$$fake_path/test1.txt'); + watcher._emit('change', './$$fake_path/test2.txt'); + + // Before the done callback, there are no pending timer events + expect(timeout.pending).toBe(0); + done(); + + // Afterwards, there is one + expect(timeout.pending).toBe(1); + } + + var watcher = watch('./$$fake_path/**/*', { delay: 10 }, cb); + + watcher._emit('change', './$$fake_path/test1.txt'); + expect(timeout.pending).toBe(1); + expect(calls).toBe(0); + + timeout.flush(10); + expect(calls).toBe(2); + }); + + + it('should cancel pending callback if FSWatcher is closed', function() { + var cb = jasmine.createSpy('callback'); + var watcher = watch('./$$fake_path/**/*', { delay: 10 }, cb); + + watcher._emit('change', './$$fake_path/test1.txt'); + expect(timeout.pending).toBe(1); + expect(cb).not.toHaveBeenCalled(); + + watcher.close(); + expect(timeout.pending).toBe(0); + }); + + + it('should cancel followup pending callback if FSWatcher is closed during task', function() { + var calls = 0; + function cb(done) { + if (++calls !== 1) return done(); + + watcher._emit('change', './$$fake_path/test2.txt'); + done(); + expect(timeout.pending).toBe(1); + watcher.close(); + expect(timeout.pending).toBe(0); + } + + var watcher = watch('./$$fake_path/**/*', { delay: 10 }, cb); + watcher._emit('change', './$$fake_path/test1.txt'); + + timeout.flush(10); + + expect(calls).toBe(1); + }); +}); + + +// setTimeout/clearTimeout mocking, mostly stolen from angular-mocks.js +function mockTimeout() { + var events = []; + var id = 0; + var now = 0; + + return { + mocks: { + setTimeout: mockSetTimeout, + clearTimeout: mockClearTimeout + }, + flush: flush, + get pending() { return events.length; } + }; + + function mockSetTimeout(fn, delay) { + delay = delay || 0; + events.push({ + time: now + delay, + fn: fn, + id: id + }); + events.sort(function(a, b) { return a.time - b.time; }); + return id++; + } + + function mockClearTimeout(id) { + for (var i = 0; i < events.length; ++i) { + if (events[i].id === id) { + events.splice(i, 1); + break; + } + } + } + + function flush(delay) { + if (delay !== undefined) now += delay; + else if (events.length) now = events[events.length - 1].time; + else throw new Error('No timer events registered'); + + while (events.length && events[0].time <= now) { + events.shift().fn(); + } + } +}