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(); } } }