From 7a96addea2d62160e97cac718cee43c844a2f880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Galfas=C3=B3?= Date: Sun, 15 Dec 2013 21:00:57 -0300 Subject: [PATCH 1/2] fix($timeout.$flush): make $flush aware of $timeout calls during $flush Fix an issue when flushing a $timeout function and, this function, calls $timeout once again. When this is the case, the new timeout function should be called at the thoerical `now` of when the function was called plus the new period --- src/ngMock/angular-mocks.js | 24 ++++++++++++++---------- test/ngMock/angular-mocksSpec.js | 28 +++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 964bd78ceae1..520931b4f837 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -106,18 +106,22 @@ angular.mock.$Browser = function() { * @param {number=} number of milliseconds to flush. See {@link #defer.now} */ self.defer.flush = function(delay) { - if (angular.isDefined(delay)) { - self.defer.now += delay; - } else { - if (self.deferredFns.length) { - self.defer.now = self.deferredFns[self.deferredFns.length - 1].time; - } else { - throw new Error('No deferred tasks to be flushed'); + try { + if (!angular.isDefined(delay)) { + if (self.deferredFns.length) { + delay = self.deferredFns[self.deferredFns.length-1].time - self.defer.now; + } else { + throw new Error('No deferred tasks to be flushed'); + } } - } - while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { - self.deferredFns.shift().fn(); + while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now + delay) { + delay -= (self.deferredFns[0].time - self.defer.now); + self.defer.now = self.deferredFns[0].time; + self.deferredFns.shift().fn(); + } + } finally { + self.defer.now += delay; } }; diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index bf75adf05d75..1cfe423020f9 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -296,8 +296,10 @@ describe('ngMock', function() { expect(counter).toBe(1); $interval.flush(1000); - expect(counter).toBe(2); + + $interval.flush(2000); + expect(counter).toBe(4); })); @@ -692,6 +694,30 @@ describe('ngMock', function() { $timeout.flush(123); expect(count).toBe(2); })); + + it('should resolve timeout functions following the timeline', inject(function($timeout) { + var count1 = 0, count2 = 0; + var iterate1 = function() { + count1++; + $timeout(iterate1, 100); + }; + var iterate2 = function() { + count2++; + $timeout(iterate2, 150); + }; + + $timeout(iterate1, 100); + $timeout(iterate2, 150); + $timeout.flush(150); + expect(count1).toBe(1); + expect(count2).toBe(1); + $timeout.flush(50); + expect(count1).toBe(2); + expect(count2).toBe(1); + $timeout.flush(400); + expect(count1).toBe(6); + expect(count2).toBe(4); + })); }); From 96c47b87730fbe0137e3017e991eb7e08ed856a6 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Fri, 27 May 2016 13:28:53 +0100 Subject: [PATCH 2/2] fix($timeout): make $flush handle new $timeouts added in $timeout callbacks If a $timeout handler calls $timeout itself, this new $timeout should be added to the mock deferred queue with a "now" time based on when the original handler was triggered. Previously it was being added with a now time of when the `$timeout.flush` method was originally called. Closes #5420 --- src/ngMock/angular-mocks.js | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js index 520931b4f837..be9e064257d3 100644 --- a/src/ngMock/angular-mocks.js +++ b/src/ngMock/angular-mocks.js @@ -106,23 +106,29 @@ angular.mock.$Browser = function() { * @param {number=} number of milliseconds to flush. See {@link #defer.now} */ self.defer.flush = function(delay) { - try { - if (!angular.isDefined(delay)) { - if (self.deferredFns.length) { - delay = self.deferredFns[self.deferredFns.length-1].time - self.defer.now; - } else { - throw new Error('No deferred tasks to be flushed'); - } - } + var nextTime; - while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now + delay) { - delay -= (self.deferredFns[0].time - self.defer.now); - self.defer.now = self.deferredFns[0].time; - self.deferredFns.shift().fn(); + if (angular.isDefined(delay)) { + // A delay was passed so compute the next time + nextTime = self.defer.now + delay; + } else { + if (self.deferredFns.length) { + // No delay was passed so set the next time so that it clears the deferred queue + nextTime = self.deferredFns[self.deferredFns.length - 1].time; + } else { + // No delay passed, but there are no deferred tasks so flush - indicates an error! + throw new Error('No deferred tasks to be flushed'); } - } finally { - self.defer.now += delay; } + + while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) { + // Increment the time and call the next deferred function + self.defer.now = self.deferredFns[0].time; + self.deferredFns.shift().fn(); + } + + // Ensure that the current time is correct + self.defer.now = nextTime; }; self.$$baseHref = '/';