diff --git a/src/.jshintrc b/src/.jshintrc
index 8c32819a6fa9..fde7535b56c4 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -86,6 +86,7 @@
"angularInit": false,
"bootstrap": false,
"getTestability": false,
+ "shutdown": false,
"snake_case": false,
"bindJQuery": false,
"assertArg": false,
diff --git a/src/Angular.js b/src/Angular.js
index bbb603f82f11..7894ed17093a 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -1692,7 +1692,7 @@ function bootstrap(element, modules, config) {
modules = modules || [];
modules.unshift(['$provide', function($provide) {
- $provide.value('$rootElement', element);
+ $provide.provider('$rootElement', rootElementProviderFactory(element));
}]);
if (config.debugInfoEnabled) {
@@ -1772,6 +1772,48 @@ function getTestability(rootElement) {
return injector.get('$$testability');
}
+function rootElementProviderFactory(rootElement) {
+ return ['$shutdownProvider', function($shutdownProvider) {
+ $shutdownProvider.register(function() {
+ if (rootElement.dealoc) {
+ rootElement.dealoc();
+ } else {
+ rootElement.find('*').removeData();
+ rootElement.removeData();
+ }
+ });
+ this.$get = function() {
+ return rootElement;
+ };
+ }];
+}
+
+function $ShutdownProvider() {
+ var fns = [];
+ this.$get = function() {
+ return function() {
+ while (fns.length) {
+ var fn = fns.shift();
+ fn();
+ }
+ };
+ };
+
+ this.register = function(fn) {
+ fns.push(fn);
+ };
+}
+
+function shutdown(element) {
+ var injector;
+
+ injector = angular.element(element).injector();
+ if (!injector) {
+ throw ngMinErr('shtdwn', 'Element not part of an app');
+ }
+ injector.get('$shutdown')();
+}
+
var SNAKE_CASE_REGEXP = /[A-Z]/g;
function snake_case(name, separator) {
separator = separator || '_';
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index 43fbe048c08e..5f012153f68a 100644
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -86,6 +86,7 @@
$$SanitizeUriProvider,
$SceProvider,
$SceDelegateProvider,
+ $ShutdownProvider,
$SnifferProvider,
$TemplateCacheProvider,
$TemplateRequestProvider,
@@ -125,6 +126,7 @@ var version = {
function publishExternalAPI(angular) {
extend(angular, {
'bootstrap': bootstrap,
+ 'shutdown': shutdown,
'copy': copy,
'extend': extend,
'merge': merge,
@@ -160,6 +162,8 @@ function publishExternalAPI(angular) {
angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) {
+ // $shutdown provider needs to be first as other providers might use it.
+ $provide.provider('$shutdown', $ShutdownProvider);
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
$provide.provider({
$$sanitizeUri: $$SanitizeUriProvider
diff --git a/src/ng/browser.js b/src/ng/browser.js
index 4a1d21bcc69e..5d5a8b9fc6b4 100644
--- a/src/ng/browser.js
+++ b/src/ng/browser.js
@@ -28,7 +28,11 @@ function Browser(window, document, $log, $sniffer) {
history = window.history,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
- pendingDeferIds = {};
+ setInterval = window.setInterval,
+ clearInterval = window.clearInterval,
+ pendingDeferIds = {},
+ currentIntervalIds = {},
+ active = true;
self.isMock = false;
@@ -79,6 +83,19 @@ function Browser(window, document, $log, $sniffer) {
}
};
+ self.shutdown = function() {
+ active = false;
+ forEach(currentIntervalIds, function(ignore, intervalId) {
+ delete currentIntervalIds[intervalId];
+ clearInterval(+intervalId);
+ });
+ forEach(pendingDeferIds, function(ignore, timeoutId) {
+ delete pendingDeferIds[timeoutId];
+ clearTimeout(+timeoutId);
+ });
+ jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
+ };
+
//////////////////////////////////////////////////////////////
// URL API
//////////////////////////////////////////////////////////////
@@ -270,16 +287,6 @@ function Browser(window, document, $log, $sniffer) {
return callback;
};
- /**
- * @private
- * Remove popstate and hashchange handler from window.
- *
- * NOTE: this api is intended for use only by $rootScope.
- */
- self.$$applicationDestroyed = function() {
- jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
- };
-
/**
* Checks whether the url has changed outside of Angular.
* Needs to be exported to be able to check for changes that have been done in sync,
@@ -321,12 +328,16 @@ function Browser(window, document, $log, $sniffer) {
*/
self.defer = function(fn, delay) {
var timeoutId;
- outstandingRequestCount++;
- timeoutId = setTimeout(function() {
- delete pendingDeferIds[timeoutId];
- completeOutstandingRequest(fn);
- }, delay || 0);
- pendingDeferIds[timeoutId] = true;
+ if (active) {
+ outstandingRequestCount++;
+ timeoutId = setTimeout(function() {
+ delete pendingDeferIds[timeoutId];
+ completeOutstandingRequest(fn);
+ }, delay || 0);
+ pendingDeferIds[timeoutId] = true;
+ } else {
+ timeoutId = 0;
+ }
return timeoutId;
};
@@ -351,11 +362,57 @@ function Browser(window, document, $log, $sniffer) {
return false;
};
+
+ /**
+ * @name $browser#interval
+ * @param {function()} fn A function, whose execution should be executed.
+ * @param {number=} interval Interval in milliseconds on how often to execute the function.
+ * @returns {*} IntervalId that can be used to cancel the task via `$browser.interval.cancel()`.
+ *
+ * @description
+ * Executes a fn asynchronously via `setInterval(fn, interval)`.
+ *
+ */
+ self.interval = function(fn, interval) {
+ var intervalId;
+ if (active) {
+ intervalId = setInterval(fn, interval);
+ currentIntervalIds[intervalId] = true;
+ } else {
+ intervalId = 0;
+ }
+ return intervalId;
+ };
+
+
+ /**
+ * @name $browser#interval.cancel
+ *
+ * @description
+ * Cancels an interval task identified with `intervalId`.
+ *
+ * @param {*} intervalId Token returned by the `$browser.interval` function.
+ * @returns {boolean} Returns `true` if the task was successfully canceled, and
+ * `false` if the task was already canceled.
+ */
+ self.interval.cancel = function(intervalId) {
+ if (currentIntervalIds[intervalId]) {
+ delete currentIntervalIds[intervalId];
+ clearInterval(intervalId);
+ return true;
+ }
+ return false;
+ };
}
-function $BrowserProvider() {
+function $BrowserProvider($shutdownProvider) {
+ var browser;
+
+ $shutdownProvider.register(function() { if (browser) { browser.shutdown(); } });
this.$get = ['$window', '$log', '$sniffer', '$document',
function($window, $log, $sniffer, $document) {
- return new Browser($window, $document, $log, $sniffer);
+ return browser = new Browser($window, $document, $log, $sniffer);
}];
}
+
+$BrowserProvider.$inject = ['$shutdownProvider'];
diff --git a/src/ng/interval.js b/src/ng/interval.js
index 36b655e2ce65..2c78506be02e 100644
--- a/src/ng/interval.js
+++ b/src/ng/interval.js
@@ -135,8 +135,6 @@ function $IntervalProvider() {
function interval(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? sliceArgs(arguments, 4) : [],
- setInterval = $window.setInterval,
- clearInterval = $window.clearInterval,
iteration = 0,
skipApply = (isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
@@ -144,7 +142,7 @@ function $IntervalProvider() {
count = isDefined(count) ? count : 0;
- promise.$$intervalId = setInterval(function tick() {
+ promise.$$intervalId = $browser.interval(function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
@@ -154,7 +152,7 @@ function $IntervalProvider() {
if (count > 0 && iteration >= count) {
deferred.resolve(iteration);
- clearInterval(promise.$$intervalId);
+ $browser.interval.cancel(promise.$$intervalId);
delete intervals[promise.$$intervalId];
}
@@ -191,7 +189,7 @@ function $IntervalProvider() {
// Interval cancels should not report as unhandled promise.
intervals[promise.$$intervalId].promise.catch(noop);
intervals[promise.$$intervalId].reject('canceled');
- $window.clearInterval(promise.$$intervalId);
+ $browser.interval.cancel(promise.$$intervalId);
delete intervals[promise.$$intervalId];
return true;
}
diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js
index eddcf61da4d6..a3dca8b58f7a 100644
--- a/src/ng/rootScope.js
+++ b/src/ng/rootScope.js
@@ -67,11 +67,13 @@
* They also provide event emission/broadcast and subscription facility. See the
* {@link guide/scope developer guide on scopes}.
*/
-function $RootScopeProvider() {
+$RootScopeProvider.$inject = ['$shutdownProvider'];
+function $RootScopeProvider($shutdownProvider) {
var TTL = 10;
var $rootScopeMinErr = minErr('$rootScope');
var lastDirtyWatch = null;
var applyAsyncId = null;
+ var $rootScope;
this.digestTtl = function(value) {
if (arguments.length) {
@@ -94,8 +96,10 @@ function $RootScopeProvider() {
return ChildScope;
}
- this.$get = ['$exceptionHandler', '$parse', '$browser',
- function($exceptionHandler, $parse, $browser) {
+ $shutdownProvider.register(function() { if ($rootScope) { $rootScope.$destroy(); } });
+
+ this.$get = ['$exceptionHandler', '$parse', '$browser', '$shutdown',
+ function($exceptionHandler, $parse, $browser, $shutdown) {
function destroyChildScope($event) {
$event.currentScope.$$destroyed = true;
@@ -907,8 +911,7 @@ function $RootScopeProvider() {
this.$$destroyed = true;
if (this === $rootScope) {
- //Remove handlers attached to window when $rootScope is removed
- $browser.$$applicationDestroyed();
+ $shutdown();
}
incrementWatchersCount(this, -this.$$watchersCount);
@@ -1308,7 +1311,7 @@ function $RootScopeProvider() {
}
};
- var $rootScope = new Scope();
+ $rootScope = new Scope();
//The internal queues. Expose them on the $rootScope for debugging/testing purposes.
var asyncQueue = $rootScope.$$asyncQueue = [];
@@ -1374,3 +1377,5 @@ function $RootScopeProvider() {
}
}];
}
+
+
diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js
index be9e064257d3..378935006fd3 100644
--- a/src/ngMock/angular-mocks.js
+++ b/src/ngMock/angular-mocks.js
@@ -23,12 +23,17 @@ angular.mock = {};
* The api of this service is the same as that of the real {@link ng.$browser $browser}, except
* that there are several helper methods available which can be used in tests.
*/
-angular.mock.$BrowserProvider = function() {
+angular.mock.$BrowserProvider = function($shutdownProvider) {
+ var browser;
+
+ $shutdownProvider.register(function() { if (browser) { browser.shutdown(); } });
this.$get = function() {
- return new angular.mock.$Browser();
+ return browser = new angular.mock.$Browser();
};
};
+angular.mock.$BrowserProvider.$inject = ['$shutdownProvider'];
+
angular.mock.$Browser = function() {
var self = this;
@@ -58,7 +63,6 @@ angular.mock.$Browser = function() {
return listener;
};
- self.$$applicationDestroyed = angular.noop;
self.$$checkUrlChange = angular.noop;
self.deferredFns = [];
@@ -135,6 +139,72 @@ angular.mock.$Browser = function() {
self.baseHref = function() {
return this.$$baseHref;
};
+
+ self.interval = function(fn, interval) {
+ self.interval.repeatFns.push({
+ nextTime:(self.interval.now + interval),
+ delay: interval,
+ fn: fn,
+ id: self.interval.nextRepeatId
+ });
+ self.interval.repeatFns.sort(function(a,b) { return a.nextTime - b.nextTime;});
+
+ return self.interval.nextRepeatId++;
+ };
+
+ self.interval.cancel = function(id) {
+ var fnIndex;
+
+ angular.forEach(self.interval.repeatFns, function(fn, index) {
+ if (fn.id === id) fnIndex = index;
+ });
+
+ if (fnIndex !== undefined) {
+ self.interval.repeatFns.splice(fnIndex, 1);
+ return true;
+ }
+
+ return false;
+ };
+
+ self.interval.flush = function(delay) {
+ var nextTime;
+
+ if (angular.isDefined(delay)) {
+ // A delay was passed so compute the next time
+ nextTime = self.interval.now + delay;
+ } else {
+ if (self.interval.repeatFns.length) {
+ // No delay was passed so set the next time so that all the existing intervals run at least once.
+ nextTime = self.repeatFns[self.repeatFns.length - 1].nextTime;
+ delay = nextTime - self.interval.now;
+ } else {
+ // No delay passed, but there are no interval tasks so flush - indicates an error!
+ throw new Error('No interval tasks to be flushed');
+ }
+ }
+
+ while (self.interval.repeatFns.length && self.interval.repeatFns[0].nextTime <= nextTime) {
+ // Increment the time and call the next deferred function
+ self.interval.now = self.interval.repeatFns[0].nextTime;
+ var task = self.interval.repeatFns[0];
+ task.fn();
+ task.nextTime += task.delay;
+ self.interval.repeatFns.sort(function(a,b) { return a.nextTime - b.nextTime;});
+ }
+
+ // Ensure that the current time is correct
+ self.interval.now = nextTime;
+
+ return delay;
+ };
+
+ self.interval.repeatFns = [];
+ self.interval.nextRepeatId = 0;
+ self.interval.now = 0;
+
+ self.shutdown = angular.noop;
+
};
angular.mock.$Browser.prototype = {
@@ -2911,10 +2981,8 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
}
angular.element.cleanData(cleanUpNodes);
- // Ensure `$destroy()` is available, before calling it
- // (a mocked `$rootScope` might not implement it (or not even be an object at all))
- var $rootScope = injector.get('$rootScope');
- if ($rootScope && $rootScope.$destroy) $rootScope.$destroy();
+ var $shutdown = injector.get('$shutdown');
+ if ($shutdown) $shutdown();
}
// clean up jquery's fragment cache
diff --git a/test/AngularSpec.js b/test/AngularSpec.js
index ec334690019d..efc7f877e5c8 100644
--- a/test/AngularSpec.js
+++ b/test/AngularSpec.js
@@ -1671,6 +1671,110 @@ describe('angular', function() {
});
});
+ describe('shutdown', function() {
+ it('should be able to shutdown an angular app', function() {
+ var element = jqLite('
bootstrap me!
');
+ angular.bootstrap(element);
+
+ angular.shutdown(element);
+ expect(element.data('$injector')).toBeUndefined();
+ expect(element.text()).toBe('bootstrap me!');
+ });
+
+ it('should be able to shutdown on any element that is part of the app', function() {
+ var element = jqLite('Bootstrap me!
');
+
+ angular.bootstrap(element);
+ angular.shutdown(element.find('span'));
+ expect(element.data('$injector')).toBeUndefined();
+ expect(element.text()).toBe('Bootstrap me!');
+ });
+
+ it('should throw if trying to shutdown an element that is not part of an app', function() {
+ var element = jqLite('Not an app
');
+
+ expect(function() {
+ angular.shutdown(element);
+ }).toThrowMinErr('ng', 'shtdwn', 'Element not part of an app');
+ });
+
+ it('should preserve the html as it is', function() {
+ var element = jqLite('Hello WorldDo not see me
');
+
+ angular.bootstrap(element);
+ angular.shutdown(element);
+ expect(element.text()).toBe('Hello World');
+ });
+
+ it('should remove all the data from all the elements', function() {
+ var element = jqLite('Bootstrap me!
');
+
+ angular.bootstrap(element);
+ element.find('span').data('foo', 'bar');
+ angular.shutdown(element);
+ expect(element.find('span').data('foo')).toBeUndefined();
+ });
+
+ describe('setTimeout and setInterval shutdown', function() {
+ var setTimeoutSpy, setInternalSpy;
+ beforeEach(inject(function($window) {
+ spyOn($window, 'setInterval').and.returnValue(42);
+ spyOn($window, 'clearInterval');
+ spyOn($window, 'setTimeout');
+ }));
+
+ it('should stop calling the window setTimeout', inject(function($window) {
+ var element = jqLite('Bootstrap me!
'),
+ injector = angular.bootstrap(element),
+ $timeout = injector.get('$timeout');
+
+ $timeout(function() {});
+ expect($window.setTimeout).toHaveBeenCalledOnce();
+ angular.shutdown(element);
+ $timeout(function() {});
+ expect($window.setTimeout).toHaveBeenCalledOnce();
+ }));
+
+ it('should cancel all active interval calls', inject(function($window) {
+ var element = jqLite('Bootstrap me!
'),
+ injector = angular.bootstrap(element),
+ $interval = injector.get('$interval');
+
+ $interval(function() {}, 100);
+ expect($window.setInterval).toHaveBeenCalledOnce();
+ expect($window.clearInterval).not.toHaveBeenCalled();
+ angular.shutdown(element);
+ expect($window.clearInterval).toHaveBeenCalledOnce();
+ expect($window.clearInterval.calls.mostRecent().args[0]).toBe(42);
+ }));
+
+ it('should not cancel intervals that were already canceled', inject(function($window) {
+ var element = jqLite('Bootstrap me!
'),
+ injector = angular.bootstrap(element),
+ $interval = injector.get('$interval'),
+ intervalPromise = $interval(function() {}, 100);
+
+ $interval.cancel(intervalPromise);
+ expect($window.setInterval).toHaveBeenCalledOnce();
+ expect($window.clearInterval).toHaveBeenCalledOnce();
+ angular.shutdown(element);
+ expect($window.setInterval).toHaveBeenCalledOnce();
+ expect($window.clearInterval).toHaveBeenCalledOnce();
+ }));
+ });
+
+ it('should destroy the root scope', function() {
+ var element = jqLite('Bootstrap me!
'),
+ injector = angular.bootstrap(element),
+ rootScope = injector.get('$rootScope'),
+ listener = jasmine.createSpy('listener');
+
+ rootScope.$on('$destroy', listener);
+ angular.shutdown(element);
+ expect(listener).toHaveBeenCalled();
+ });
+ });
+
describe('angular service', function() {
it('should override services', function() {
diff --git a/test/ng/browserSpecs.js b/test/ng/browserSpecs.js
index 9140bcb71e03..2664824ee55b 100755
--- a/test/ng/browserSpecs.js
+++ b/test/ng/browserSpecs.js
@@ -738,7 +738,7 @@ describe('browser', function() {
browser.onUrlChange(callback);
fakeWindow.location.href = 'http://server/new';
- browser.$$applicationDestroyed();
+ browser.shutdown();
fakeWindow.fire('popstate');
expect(callback).not.toHaveBeenCalled();
diff --git a/test/ng/intervalSpec.js b/test/ng/intervalSpec.js
index 300cef7e1f2e..73cf2cdb06ae 100644
--- a/test/ng/intervalSpec.js
+++ b/test/ng/intervalSpec.js
@@ -4,109 +4,63 @@ describe('$interval', function() {
/* global $IntervalProvider: false */
beforeEach(module(function($provide) {
- var repeatFns = [],
- nextRepeatId = 0,
- now = 0,
- $window;
-
- $window = {
- setInterval: function(fn, delay, count) {
- repeatFns.push({
- nextTime:(now + delay),
- delay: delay,
- fn: fn,
- id: nextRepeatId
- });
- repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
-
- return nextRepeatId++;
- },
-
- clearInterval: function(id) {
- var fnIndex;
-
- angular.forEach(repeatFns, function(fn, index) {
- if (fn.id === id) fnIndex = index;
- });
-
- if (isDefined(fnIndex)) {
- repeatFns.splice(fnIndex, 1);
- return true;
- }
-
- return false;
- },
-
- flush: function(millis) {
- now += millis;
- while (repeatFns.length && repeatFns[0].nextTime <= now) {
- var task = repeatFns[0];
- task.fn();
- task.nextTime += task.delay;
- repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;});
- }
- return millis;
- }
- };
-
$provide.provider('$interval', $IntervalProvider);
- $provide.value('$window', $window);
}));
- it('should run tasks repeatedly', inject(function($interval, $window) {
+ it('should run tasks repeatedly', inject(function($interval, $browser) {
var counter = 0;
$interval(function() { counter++; }, 1000);
expect(counter).toBe(0);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(1);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(2);
}));
it('should call $apply after each task is executed',
- inject(function($interval, $rootScope, $window) {
+ inject(function($interval, $rootScope, $browser) {
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(noop, 1000);
expect(applySpy).not.toHaveBeenCalled();
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(applySpy).toHaveBeenCalledOnce();
applySpy.calls.reset();
$interval(noop, 1000);
$interval(noop, 1000);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(applySpy).toHaveBeenCalledTimes(3);
}));
it('should NOT call $apply if invokeApply is set to false',
- inject(function($interval, $rootScope, $window) {
+ inject(function($interval, $rootScope, $browser) {
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(noop, 1000, 0, false);
expect(applySpy).not.toHaveBeenCalled();
- $window.flush(2000);
+ $browser.interval.flush(2000);
expect(applySpy).not.toHaveBeenCalled();
}));
it('should NOT call $evalAsync or $digest if invokeApply is set to false',
- inject(function($interval, $rootScope, $window, $timeout) {
+ inject(function($interval, $rootScope, $browser, $timeout) {
var evalAsyncSpy = spyOn($rootScope, '$evalAsync').and.callThrough();
var digestSpy = spyOn($rootScope, '$digest').and.callThrough();
var notifySpy = jasmine.createSpy('notify');
$interval(notifySpy, 1000, 1, false);
- $window.flush(2000);
+ $browser.interval.flush(2000);
$timeout.flush(); // flush $browser.defer() timeout
expect(notifySpy).toHaveBeenCalledOnce();
@@ -128,50 +82,50 @@ describe('$interval', function() {
});
});
- inject(function($interval, $window) {
+ inject(function($interval, $browser) {
var counter = 0;
$interval(function() { counter++; }, 1000);
expect(counter).toBe(0);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(1);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(2);
});
});
- it('should allow you to specify the delay time', inject(function($interval, $window) {
+ it('should allow you to specify the delay time', inject(function($interval, $browser) {
var counter = 0;
$interval(function() { counter++; }, 123);
expect(counter).toBe(0);
- $window.flush(122);
+ $browser.interval.flush(122);
expect(counter).toBe(0);
- $window.flush(1);
+ $browser.interval.flush(1);
expect(counter).toBe(1);
}));
- it('should allow you to specify a number of iterations', inject(function($interval, $window) {
+ it('should allow you to specify a number of iterations', inject(function($interval, $browser) {
var counter = 0;
$interval(function() {counter++;}, 1000, 2);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(1);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(2);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(counter).toBe(2);
}));
- it('should allow you to specify a number of arguments', inject(function($interval, $window) {
+ it('should allow you to specify a number of arguments', inject(function($interval, $browser) {
var task1 = jasmine.createSpy('task1'),
task2 = jasmine.createSpy('task2'),
task3 = jasmine.createSpy('task3');
@@ -179,7 +133,7 @@ describe('$interval', function() {
$interval(task2, 1000, 2, true, 'Task2');
$interval(task3, 1000, 2, true, 'I', 'am', 'a', 'Task3', 'spy');
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(task1).toHaveBeenCalledWith('Task1');
expect(task2).toHaveBeenCalledWith('Task2');
expect(task3).toHaveBeenCalledWith('I', 'am', 'a', 'Task3', 'spy');
@@ -188,7 +142,7 @@ describe('$interval', function() {
task2.calls.reset();
task3.calls.reset();
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(task1).toHaveBeenCalledWith('Task1');
expect(task2).toHaveBeenCalledWith('Task2');
expect(task3).toHaveBeenCalledWith('I', 'am', 'a', 'Task3', 'spy');
@@ -197,7 +151,7 @@ describe('$interval', function() {
it('should return a promise which will be updated with the count on each iteration',
- inject(function($interval, $window) {
+ inject(function($interval, $browser) {
var log = [],
promise = $interval(function() { log.push('tick'); }, 1000);
@@ -206,16 +160,16 @@ describe('$interval', function() {
function(note) { log.push('promise update: ' + note); });
expect(log).toEqual([]);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(log).toEqual(['tick', 'promise update: 0']);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(log).toEqual(['tick', 'promise update: 0', 'tick', 'promise update: 1']);
}));
it('should return a promise which will be resolved after the specified number of iterations',
- inject(function($interval, $window) {
+ inject(function($interval, $browser) {
var log = [],
promise = $interval(function() { log.push('tick'); }, 1000, 2);
@@ -224,9 +178,9 @@ describe('$interval', function() {
function(note) { log.push('promise update: ' + note); });
expect(log).toEqual([]);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(log).toEqual(['tick', 'promise update: 0']);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(log).toEqual([
'tick', 'promise update: 0', 'tick', 'promise update: 1', 'promise success: 2'
@@ -242,39 +196,39 @@ describe('$interval', function() {
it('should delegate exception to the $exceptionHandler service', inject(
- function($interval, $exceptionHandler, $window) {
+ function($interval, $exceptionHandler, $browser) {
$interval(function() { throw "Test Error"; }, 1000);
expect($exceptionHandler.errors).toEqual([]);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect($exceptionHandler.errors).toEqual(["Test Error"]);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect($exceptionHandler.errors).toEqual(["Test Error", "Test Error"]);
}));
it('should call $apply even if an exception is thrown in callback', inject(
- function($interval, $rootScope, $window) {
+ function($interval, $rootScope, $browser) {
var applySpy = spyOn($rootScope, '$apply').and.callThrough();
$interval(function() { throw "Test Error"; }, 1000);
expect(applySpy).not.toHaveBeenCalled();
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(applySpy).toHaveBeenCalled();
}));
it('should still update the interval promise when an exception is thrown',
- inject(function($interval, $window) {
+ inject(function($interval, $browser) {
var log = [],
promise = $interval(function() { throw "Some Error"; }, 1000);
promise.then(function(value) { log.push('promise success: ' + value); },
function(err) { log.push('promise error: ' + err); },
function(note) { log.push('promise update: ' + note); });
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(log).toEqual(['promise update: 0']);
}));
@@ -282,7 +236,7 @@ describe('$interval', function() {
describe('cancel', function() {
- it('should cancel tasks', inject(function($interval, $window) {
+ it('should cancel tasks', inject(function($interval, $browser) {
var task1 = jasmine.createSpy('task1', 1000),
task2 = jasmine.createSpy('task2', 1000),
task3 = jasmine.createSpy('task3', 1000),
@@ -294,7 +248,7 @@ describe('$interval', function() {
$interval.cancel(promise3);
$interval.cancel(promise1);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(task1).not.toHaveBeenCalled();
expect(task2).toHaveBeenCalledOnce();
@@ -302,7 +256,7 @@ describe('$interval', function() {
}));
- it('should cancel the promise', inject(function($interval, $rootScope, $window) {
+ it('should cancel the promise', inject(function($interval, $rootScope, $browser) {
var promise = $interval(noop, 1000),
log = [];
promise.then(function(value) { log.push('promise success: ' + value); },
@@ -310,9 +264,9 @@ describe('$interval', function() {
function(note) { log.push('promise update: ' + note); });
expect(log).toEqual([]);
- $window.flush(1000);
+ $browser.interval.flush(1000);
$interval.cancel(promise);
- $window.flush(1000);
+ $browser.interval.flush(1000);
$rootScope.$apply(); // For resolving the promise -
// necessary since q uses $rootScope.evalAsync.
@@ -321,13 +275,13 @@ describe('$interval', function() {
it('should return true if a task was successfully canceled',
- inject(function($interval, $window) {
+ inject(function($interval, $browser) {
var task1 = jasmine.createSpy('task1'),
task2 = jasmine.createSpy('task2'),
promise1, promise2;
promise1 = $interval(task1, 1000, 1);
- $window.flush(1000);
+ $browser.interval.flush(1000);
promise2 = $interval(task2, 1000, 1);
expect($interval.cancel(promise1)).toBe(false);
@@ -341,19 +295,19 @@ describe('$interval', function() {
}));
});
- describe('$window delegation', function() {
- it('should use $window.setInterval instead of the global function', inject(function($interval, $window) {
- var setIntervalSpy = spyOn($window, 'setInterval');
+ describe('$browser delegation', function() {
+ it('should use $borwser.interval instead of the global function', inject(function($interval, $browser) {
+ var setIntervalSpy = spyOn($browser, 'interval');
$interval(noop, 1000);
expect(setIntervalSpy).toHaveBeenCalled();
}));
- it('should use $window.clearInterval instead of the global function', inject(function($interval, $window) {
- var clearIntervalSpy = spyOn($window, 'clearInterval');
+ it('should use $browser.interval.cancel instead of the global function', inject(function($interval, $browser) {
+ var clearIntervalSpy = spyOn($browser.interval, 'cancel');
$interval(noop, 1000, 1);
- $window.flush(1000);
+ $browser.interval.flush(1000);
expect(clearIntervalSpy).toHaveBeenCalled();
clearIntervalSpy.calls.reset();
diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js
index 70e565b7b434..019ee6b6eb9a 100644
--- a/test/ng/rootScopeSpec.js
+++ b/test/ng/rootScopeSpec.js
@@ -1075,10 +1075,10 @@ describe('Scope', function() {
}));
- it('should call $browser.$$applicationDestroyed when destroying rootScope', inject(function($rootScope, $browser) {
- spyOn($browser, '$$applicationDestroyed');
+ it('should call shutdown when destroying rootScope', inject(function($rootScope, $browser) {
+ spyOn($browser, 'shutdown');
$rootScope.$destroy();
- expect($browser.$$applicationDestroyed).toHaveBeenCalledOnce();
+ expect($browser.shutdown).toHaveBeenCalledOnce();
}));