diff options
| -rw-r--r-- | src/Browser.js | 42 | ||||
| -rw-r--r-- | src/angular-mocks.js | 19 | ||||
| -rw-r--r-- | test/BrowserSpecs.js | 36 |
3 files changed, 86 insertions, 11 deletions
diff --git a/src/Browser.js b/src/Browser.js index 55b65471..58ad22c8 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -40,6 +40,8 @@ function Browser(window, document, body, XHR, $log) { rawDocument = document[0], location = window.location, setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}, lastLocationUrl; self.isMock = false; @@ -163,15 +165,12 @@ function Browser(window, document, body, XHR, $log) { * @returns {function()} the added function */ self.addPollFn = function(fn) { - if (!pollTimeout) startPoller(100, setTimeout); + if (isUndefined(pollTimeout)) startPoller(100, setTimeout); pollFns.push(fn); return fn; }; /** - * @name angular.service.$browser#startPoller - * @methodOf angular.service.$browser - * * @param {number} interval How often should browser call poll functions (ms) * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. * @@ -339,20 +338,49 @@ function Browser(window, document, body, XHR, $log) { * @methodOf angular.service.$browser * @param {function()} fn A function, who's execution should be defered. * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. * * @description * Executes a fn asynchroniously via `setTimeout(fn, delay)`. * * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using - * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed via - * `$browser.defer.flush()`. + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. * */ self.defer = function(fn, delay) { + var timeoutId; outstandingRequestCount++; - setTimeout(function() { completeOutstandingRequest(fn); }, delay || 0); + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; }; + + /** + * @workInProgress + * @ngdoc method + * @name angular.service.$browser.defer#cancel + * @methodOf angular.service.$browser.defer + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled. + * + * @description + * Cancels a defered task identified with `deferId`. + */ + + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + }; + + ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// diff --git a/src/angular-mocks.js b/src/angular-mocks.js index a4941288..20423f05 100644 --- a/src/angular-mocks.js +++ b/src/angular-mocks.js @@ -281,15 +281,32 @@ function MockBrowser() { self.cookieHash = {}; self.lastCookieHash = {}; self.deferredFns = []; + self.deferredNextId = 0; self.defer = function(fn, delay) { delay = delay || 0; - self.deferredFns.push({time:(self.defer.now + delay), fn:fn}); + self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); self.deferredFns.sort(function(a,b){ return a.time - b.time;}); + return self.deferredNextId++; }; + self.defer.now = 0; + + self.defer.cancel = function(deferId) { + var fnIndex; + + forEach(self.deferredFns, function(fn, index) { + if (fn.id === deferId) fnIndex = index; + }); + + if (fnIndex) { + self.deferredFns.splice(fnIndex, 1); + } + } + + self.defer.flush = function(delay) { if (angular.isDefined(delay)) { self.defer.now += delay; diff --git a/test/BrowserSpecs.js b/test/BrowserSpecs.js index b4ad688b..40e983da 100644 --- a/test/BrowserSpecs.js +++ b/test/BrowserSpecs.js @@ -5,8 +5,11 @@ describe('browser', function(){ var browser, fakeWindow, xhr, logs, scripts, removedScripts, setTimeoutQueue; function fakeSetTimeout(fn) { - setTimeoutQueue.push(fn); - return Math.random(); + return setTimeoutQueue.push(fn) - 1; //return position in the queue + } + + function fakeClearTimeout(deferId) { + setTimeoutQueue[deferId] = noop; //replace fn with noop to preserve other deferId indexes } fakeSetTimeout.flush = function() { @@ -25,7 +28,8 @@ describe('browser', function(){ xhr = null; fakeWindow = { location: {href:"http://server"}, - setTimeout: fakeSetTimeout + setTimeout: fakeSetTimeout, + clearTimeout: fakeClearTimeout }; var fakeBody = [{appendChild: function(node){scripts.push(node);}, @@ -161,6 +165,32 @@ describe('browser', function(){ fakeSetTimeout.flush(); expect(callback).toHaveBeenCalled(); }); + + + it('should return unique deferId', function() { + var deferId1 = browser.defer(noop), + deferId2 = browser.defer(noop); + + expect(deferId1).toBeDefined(); + expect(deferId2).toBeDefined(); + expect(deferId1).not.toEqual(deferId2); + }) + + + describe('cancel', function() { + it('should allow tasks to be canceled with returned deferId', function() { + var log = [], + deferId1 = browser.defer(function() { log.push('cancel me') }), + deferId2 = browser.defer(function() { log.push('ok') }), + deferId3 = browser.defer(function() { log.push('cancel me, now!') }); + + expect(log).toEqual([]); + browser.defer.cancel(deferId1); + browser.defer.cancel(deferId3); + fakeSetTimeout.flush(); + expect(log).toEqual(['ok']); + }); + }); }); |
