toasterContainerSpec.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. /* global describe global it global beforeEach global angular global jasmine global inject global expect global spyOn */
  2. 'use strict';
  3. var rootScope, toaster, $compile;
  4. describe('toasterContainer', function () {
  5. beforeEach(function () {
  6. module('toaster');
  7. // inject the toaster service
  8. inject(function (_toaster_, _$rootScope_, _$compile_) {
  9. toaster = _toaster_;
  10. rootScope = _$rootScope_;
  11. $compile = _$compile_;
  12. });
  13. });
  14. it('should pop a toast via individual parameters', function () {
  15. var container = compileContainer();
  16. var scope = container.scope();
  17. toaster.pop('info', 'test', 'test');
  18. expect(scope.toasters.length).toBe(1);
  19. });
  20. it('should unsubscribe events on $destroy if handlers exist', function () {
  21. var toasterEventRegistry;
  22. inject(function (_toasterEventRegistry_) {
  23. toasterEventRegistry = _toasterEventRegistry_;
  24. });
  25. var container = compileContainer();
  26. var scope = container.scope();
  27. spyOn(toasterEventRegistry, 'unsubscribeToNewToastEvent').and.callThrough();
  28. spyOn(toasterEventRegistry, 'unsubscribeToClearToastsEvent').and.callThrough();
  29. scope.$destroy();
  30. expect(toasterEventRegistry.unsubscribeToNewToastEvent).toHaveBeenCalled();
  31. expect(toasterEventRegistry.unsubscribeToClearToastsEvent).toHaveBeenCalled();
  32. });
  33. describe('addToast', function () {
  34. it('should default to icon-class config value if toast.type not found in icon-classes', function () {
  35. var toasterConfig;
  36. inject(function (_toasterConfig_) {
  37. toasterConfig = _toasterConfig_;
  38. });
  39. compileContainer();
  40. expect(toasterConfig['icon-class']).toBe('toast-info');
  41. toaster.pop({ type: 'invalid' });
  42. rootScope.$digest();
  43. expect(toaster.toast.type).toBe('toast-info');
  44. });
  45. it('should allow subsequent duplicates if prevent-duplicates is not set', function () {
  46. var container = compileContainer();
  47. var scope = container.scope();
  48. expect(scope.toasters.length).toBe(0);
  49. toaster.pop({ type: 'info', title: 'title', body: 'body' });
  50. toaster.pop({ type: 'info', title: 'title', body: 'body' });
  51. rootScope.$digest();
  52. expect(scope.toasters.length).toBe(2);
  53. });
  54. it('should not allow subsequent duplicates if prevent-duplicates is true without toastId param', function () {
  55. var container = angular.element(
  56. '<toaster-container toaster-options="{\'prevent-duplicates\': true}"></toaster-container>');
  57. $compile(container)(rootScope);
  58. rootScope.$digest();
  59. var scope = container.scope();
  60. expect(scope.toasters.length).toBe(0);
  61. toaster.pop({ type: 'info', title: 'title', body: 'body' });
  62. toaster.pop({ type: 'info', title: 'title', body: 'body' });
  63. rootScope.$digest();
  64. expect(scope.toasters.length).toBe(1);
  65. });
  66. it('should allow subsequent duplicates if prevent-duplicates is true with unique toastId params', function () {
  67. var container = angular.element(
  68. '<toaster-container toaster-options="{\'prevent-duplicates\': true}"></toaster-container>');
  69. $compile(container)(rootScope);
  70. rootScope.$digest();
  71. var scope = container.scope();
  72. expect(scope.toasters.length).toBe(0);
  73. toaster.pop({ type: 'info', title: 'title', body: 'body', toastId: 1 });
  74. toaster.pop({ type: 'info', title: 'title', body: 'body', toastId: 2 });
  75. rootScope.$digest();
  76. expect(scope.toasters.length).toBe(2);
  77. });
  78. it('should not allow subsequent duplicates if prevent-duplicates is true with identical toastId params', function () {
  79. var container = angular.element(
  80. '<toaster-container toaster-options="{\'prevent-duplicates\': true}"></toaster-container>');
  81. $compile(container)(rootScope);
  82. rootScope.$digest();
  83. var scope = container.scope();
  84. expect(scope.toasters.length).toBe(0);
  85. toaster.pop({ type: 'info', title: 'title', body: 'body', toastId: 1 });
  86. toaster.pop({ type: 'info', title: 'title', body: 'body', toastId: 1 });
  87. rootScope.$digest();
  88. expect(scope.toasters.length).toBe(1);
  89. });
  90. it('should not render the close button if showCloseButton is false', function () {
  91. var container = compileContainer();
  92. toaster.pop({ type: 'info', body: 'With a close button' });
  93. rootScope.$digest();
  94. expect(container.find('button')[0]).toBeUndefined();
  95. });
  96. it('should use the default close html if toast.closeHtml is undefined', function () {
  97. var container = compileContainer();
  98. toaster.pop({ type: 'info', body: 'With a close button', showCloseButton: true });
  99. rootScope.$digest();
  100. var buttons = container.find('button');
  101. expect(buttons.length).toBe(1);
  102. expect(buttons[0].outerHTML).toBe('<button class="toast-close-button" type="button">×</button>');
  103. });
  104. it('should use the toast.closeHtml argument if passed', function () {
  105. var container = compileContainer();
  106. toaster.pop({ type: 'info', body: 'With a close button', showCloseButton: true,
  107. closeHtml: '<button>Close</button>'
  108. });
  109. rootScope.$digest();
  110. var buttons = container.find('button');
  111. expect(buttons.length).toBe(1);
  112. expect(buttons[0].outerHTML).toBe('<button>Close</button>');
  113. });
  114. it('should render toast.closeHtml argument if not a button element', function () {
  115. var container = compileContainer();
  116. toaster.pop({ type: 'info', body: 'With close text', showCloseButton: true,
  117. closeHtml: '<span>Close</span>'
  118. });
  119. rootScope.$digest();
  120. var spans = container.find('span');
  121. expect(spans.length).toBe(1);
  122. expect(spans[0].outerHTML).toBe('<span>Close</span>');
  123. });
  124. it('should show the close button if mergedConfig close-button is an object set to true for toast-info', function () {
  125. var container = angular.element(
  126. '<toaster-container toaster-options="{\'close-button\': {\'toast-info\': true}}"></toaster-container>');
  127. $compile(container)(rootScope);
  128. rootScope.$digest();
  129. toaster.pop({ type: 'info' });
  130. rootScope.$digest();
  131. var buttons = container.find('button');
  132. expect(buttons.length).toBe(1);
  133. expect(buttons[0].outerHTML).toBe('<button class="toast-close-button" type="button">×</button>');
  134. });
  135. it('should not render the close button if mergedConfig close-button type cannot be found', function () {
  136. var container = angular.element(
  137. '<toaster-container toaster-options="{\'close-button\': {\'toast-invalid\': true}}"></toaster-container>');
  138. $compile(container)(rootScope);
  139. rootScope.$digest();
  140. toaster.pop({ type: 'info' });
  141. rootScope.$digest();
  142. var buttons = container.find('button');
  143. expect(buttons.length).toBe(0);
  144. expect(buttons[0]).toBeUndefined();
  145. });
  146. it('should not render the close button if mergedConfig close-button is not an object', function () {
  147. var container = angular.element(
  148. '<toaster-container toaster-options="{\'close-button\': 1 }"></toaster-container>');
  149. $compile(container)(rootScope);
  150. rootScope.$digest();
  151. toaster.pop({ type: 'info' });
  152. rootScope.$digest();
  153. var buttons = container.find('button');
  154. expect(buttons.length).toBe(0);
  155. expect(buttons[0]).toBeUndefined();
  156. });
  157. it('should render trustedHtml bodyOutputType', function () {
  158. var container = compileContainer();
  159. toaster.pop({ bodyOutputType: 'trustedHtml', body: '<section>Body</section>' });
  160. rootScope.$digest();
  161. var body = container.find('section');
  162. expect(body.length).toBe(1);
  163. expect(body[0].outerHTML).toBe('<section>Body</section>');
  164. });
  165. it('should render template bodyOutputType when body is passed', function () {
  166. inject(function($templateCache) {
  167. $templateCache.put('/templatepath/template.html', '<section>Template</section>');
  168. });
  169. var container = compileContainer();
  170. toaster.pop({ bodyOutputType: 'template', body: '/templatepath/template.html' });
  171. rootScope.$digest();
  172. expect(toaster.toast.body).toBe('/templatepath/template.html');
  173. var body = container.find('section');
  174. expect(body.length).toBe(1);
  175. expect(body[0].outerHTML).toBe('<section class="ng-scope">Template</section>');
  176. });
  177. it('should render default template bodyOutputType when body is not passed', function () {
  178. inject(function($templateCache) {
  179. $templateCache.put('toasterBodyTmpl.html', '<section>Template</section>');
  180. });
  181. var container = compileContainer();
  182. toaster.pop({ bodyOutputType: 'template' });
  183. rootScope.$digest();
  184. expect(toaster.toast.bodyTemplate).toBe('toasterBodyTmpl.html');
  185. var body = container.find('section');
  186. expect(body.length).toBe(1);
  187. expect(body[0].outerHTML).toBe('<section class="ng-scope">Template</section>');
  188. });
  189. it('should render templateWithData bodyOutputType when body is passed', function () {
  190. inject(function($templateCache) {
  191. $templateCache.put('template.html', '<section>Template {{toaster.data}}</section>');
  192. });
  193. var container = compileContainer();
  194. toaster.pop({ bodyOutputType: 'templateWithData', body: "{template: 'template.html', data: 123 }" });
  195. rootScope.$digest();
  196. var body = container.find('section');
  197. expect(body.length).toBe(1);
  198. expect(body[0].outerHTML).toBe('<section class="ng-binding ng-scope">Template 123</section>');
  199. });
  200. it('should throw exception for default templateWithData bodyOutputType when body is not passed', function () {
  201. // TODO: If the default fallback template cannot be parsed to an object
  202. // composed of template and data, an exception is thrown. This seems to
  203. // be undesirable behavior. A clearer exception should be thrown, or better
  204. // handling should be handled, or the fallback option should be removed.
  205. inject(function($templateCache) {
  206. $templateCache.put('template.html', '<section>Template {{toaster.data}}</section>');
  207. });
  208. compileContainer();
  209. var hasException = false;
  210. try {
  211. toaster.pop({ bodyOutputType: 'templateWithData' });
  212. } catch (e) {
  213. expect(e.message).toBe("Cannot read property 'template' of undefined");
  214. hasException = true;
  215. }
  216. expect(hasException).toBe(true);
  217. });
  218. it('should remove first in toast if limit is met and newest-on-top is true', function () {
  219. var container = angular.element(
  220. '<toaster-container toaster-options="{\'limit\': 2, \'newest-on-top\': true }"></toaster-container>');
  221. $compile(container)(rootScope);
  222. rootScope.$digest();
  223. var scope = container.scope();
  224. toaster.pop({ type: 'info', body: 'first' });
  225. toaster.pop({ type: 'info', body: 'second' });
  226. rootScope.$digest();
  227. expect(scope.toasters.length).toBe(2);
  228. expect(scope.toasters[0].body).toBe('second');
  229. expect(scope.toasters[1].body).toBe('first');
  230. toaster.pop({ type: 'info', body: 'third' });
  231. rootScope.$digest();
  232. expect(scope.toasters.length).toBe(2);
  233. expect(scope.toasters[0].body).toBe('third');
  234. expect(scope.toasters[1].body).toBe('second');
  235. });
  236. it('should remove last in toast if limit is met and newest-on-top is false', function () {
  237. var container = angular.element(
  238. '<toaster-container toaster-options="{\'limit\': 2, \'newest-on-top\': false }"></toaster-container>');
  239. $compile(container)(rootScope);
  240. rootScope.$digest();
  241. var scope = container.scope();
  242. toaster.pop({ type: 'info', body: 'first' });
  243. toaster.pop({ type: 'info', body: 'second' });
  244. rootScope.$digest();
  245. expect(scope.toasters.length).toBe(2);
  246. expect(scope.toasters[0].body).toBe('first');
  247. expect(scope.toasters[1].body).toBe('second');
  248. toaster.pop({ type: 'info', body: 'third' });
  249. rootScope.$digest();
  250. expect(scope.toasters.length).toBe(2);
  251. expect(scope.toasters[0].body).toBe('second');
  252. expect(scope.toasters[1].body).toBe('third');
  253. });
  254. });
  255. describe('removeToast', function () {
  256. it('should not remove toast if id does not match a toast id', function() {
  257. var container = compileContainer();
  258. var scope = container.scope();
  259. toaster.pop({ type: 'info', body: 'toast 1' });
  260. toaster.pop({ type: 'info', body: 'toast 2' });
  261. rootScope.$digest();
  262. expect(scope.toasters.length).toBe(2);
  263. expect(scope.toasters[0].id).toBe(2)
  264. expect(scope.toasters[1].id).toBe(1)
  265. scope.removeToast(3);
  266. rootScope.$digest();
  267. expect(scope.toasters.length).toBe(2);
  268. });
  269. it('should invoke onHideCallback if it exists when toast is removed', function () {
  270. var container = compileContainer();
  271. var scope = container.scope();
  272. var mock = {
  273. callback : function () { }
  274. };
  275. spyOn(mock, 'callback');
  276. toaster.pop({ type: 'info', body: 'toast 1', onHideCallback: mock.callback });
  277. rootScope.$digest();
  278. scope.removeToast(1);
  279. rootScope.$digest();
  280. expect(mock.callback).toHaveBeenCalled();
  281. });
  282. });
  283. describe('scope._onNewTest', function () {
  284. it('should not add toast if toasterId is passed to scope._onNewToast but toasterId is not set via config', function () {
  285. var container = compileContainer();
  286. var scope = container.scope();
  287. expect(scope.config.toasterId).toBeUndefined();
  288. toaster.pop({ type: 'info', body: 'toast 1', toasterId: 1 });
  289. rootScope.$digest();
  290. expect(scope.toasters.length).toBe(0);
  291. });
  292. it('should add toast if toasterId is passed to scope._onNewToast and toasterId is set via config', function () {
  293. var container = angular.element(
  294. '<toaster-container toaster-options="{ \'toaster-id\': 1 }"></toaster-container>');
  295. $compile(container)(rootScope);
  296. rootScope.$digest();
  297. var scope = container.scope();
  298. expect(scope.config.toasterId).toBe(1);
  299. toaster.pop({ type: 'info', body: 'toast 1', toasterId: 1 });
  300. rootScope.$digest();
  301. expect(scope.toasters.length).toBe(1);
  302. });
  303. it('should add toasts to their respective container based on toasterId', function () {
  304. var container1 = angular.element(
  305. '<toaster-container toaster-options="{ \'toaster-id\': 1 }"></toaster-container>');
  306. var container2 = angular.element(
  307. '<toaster-container toaster-options="{ \'toaster-id\': 2 }"></toaster-container>');
  308. $compile(container1)(rootScope);
  309. $compile(container2)(rootScope);
  310. rootScope.$digest();
  311. var scope1 = container1.scope();
  312. var scope2 = container2.scope();
  313. toaster.pop({ type: 'info', body: 'toast 1', toasterId: 1 });
  314. toaster.pop({ type: 'info', body: 'toast 2', toasterId: 2 });
  315. rootScope.$digest();
  316. expect(scope1.toasters.length).toBe(1);
  317. expect(scope2.toasters.length).toBe(1);
  318. });
  319. });
  320. describe('scope._onClearToasts', function (){
  321. it('should remove all toasts from all containers if toasterId is *', function () {
  322. var container1 = angular.element(
  323. '<toaster-container toaster-options="{ \'toaster-id\': 1 }"></toaster-container>');
  324. var container2 = angular.element(
  325. '<toaster-container toaster-options="{ \'toaster-id\': 2 }"></toaster-container>');
  326. $compile(container1)(rootScope);
  327. $compile(container2)(rootScope);
  328. rootScope.$digest();
  329. var scope1 = container1.scope();
  330. var scope2 = container2.scope();
  331. toaster.pop({ type: 'info', body: 'toast 1', toasterId: 1 });
  332. toaster.pop({ type: 'info', body: 'toast 2', toasterId: 2 });
  333. rootScope.$digest();
  334. expect(scope1.toasters.length).toBe(1);
  335. expect(scope2.toasters.length).toBe(1);
  336. toaster.clear('*');
  337. rootScope.$digest();
  338. expect(scope1.toasters.length).toBe(0);
  339. expect(scope2.toasters.length).toBe(0);
  340. });
  341. it('should remove all toasts from all containers if config.toasterId and toastId are undefined', function () {
  342. var container1 = angular.element(
  343. '<toaster-container toaster-options="{ \'close-button\': false }"></toaster-container>');
  344. var container2 = angular.element(
  345. '<toaster-container toaster-options="{ \'close-button\': true }" ></toaster-container>');
  346. $compile(container1)(rootScope);
  347. $compile(container2)(rootScope);
  348. rootScope.$digest();
  349. var scope1 = container1.scope();
  350. var scope2 = container2.scope();
  351. toaster.pop({ type: 'info', body: 'toast 1' });
  352. toaster.pop({ type: 'info', body: 'toast 2' });
  353. rootScope.$digest();
  354. // since there are two separate instances of the container
  355. // without a toasterId, both receive the newToast event
  356. expect(scope1.toasters.length).toBe(2);
  357. expect(scope2.toasters.length).toBe(2);
  358. toaster.clear();
  359. rootScope.$digest();
  360. expect(scope1.toasters.length).toBe(0);
  361. expect(scope2.toasters.length).toBe(0);
  362. });
  363. it('should not remove by toasterId / toastId from the correct container if toast.toasterId is defined and toast.toastId is undefined', function () {
  364. var container1 = angular.element(
  365. '<toaster-container toaster-options="{ \'toaster-id\': 1 }"></toaster-container>');
  366. var container2 = angular.element(
  367. '<toaster-container toaster-options="{ \'toaster-id\': 2 }"></toaster-container>');
  368. $compile(container1)(rootScope);
  369. $compile(container2)(rootScope);
  370. rootScope.$digest();
  371. var scope1 = container1.scope();
  372. var scope2 = container2.scope();
  373. // removeAllToasts explicitly looks for toast.uid, which is only set
  374. // if toastId is passed as a parameter
  375. toaster.pop({ type: 'info', body: 'toast 1', toasterId: 1 });
  376. toaster.pop({ type: 'info', body: 'toast 2', toasterId: 2 });
  377. toaster.pop({ type: 'info', body: 'toast 3', toasterId: 2 });
  378. rootScope.$digest();
  379. expect(scope1.toasters.length).toBe(1);
  380. expect(scope2.toasters.length).toBe(2);
  381. toaster.clear(2, 1);
  382. rootScope.$digest();
  383. expect(scope1.toasters.length).toBe(1);
  384. expect(scope2.toasters.length).toBe(2);
  385. });
  386. it('should remove by toasterId / toastId from the correct container if toasterId is defined and toastId is defined', function () {
  387. var container1 = angular.element(
  388. '<toaster-container toaster-options="{ \'toaster-id\': 1 }"></toaster-container>');
  389. var container2 = angular.element(
  390. '<toaster-container toaster-options="{ \'toaster-id\': 2 }"></toaster-container>');
  391. $compile(container1)(rootScope);
  392. $compile(container2)(rootScope);
  393. rootScope.$digest();
  394. var scope1 = container1.scope();
  395. var scope2 = container2.scope();
  396. // removeAllToasts explicitly looks for toast.uid, which is only set
  397. // if toastId is passed as a parameter
  398. toaster.pop({ type: 'info', body: 'toast 1', toasterId: 1, toastId: 1 });
  399. toaster.pop({ type: 'info', body: 'toast 2', toasterId: 2, toastId: 1 });
  400. toaster.pop({ type: 'info', body: 'toast 3', toasterId: 2, toastId: 2 });
  401. rootScope.$digest();
  402. expect(scope1.toasters.length).toBe(1);
  403. expect(scope2.toasters.length).toBe(2);
  404. toaster.clear(2, 1);
  405. rootScope.$digest();
  406. expect(scope1.toasters.length).toBe(1);
  407. expect(scope2.toasters.length).toBe(1);
  408. });
  409. });
  410. });
  411. describe('toasterContainer', function () {
  412. var $interval, $intervalSpy;
  413. inject(function (_$interval_) {
  414. $interval = _$interval_;
  415. });
  416. beforeEach(function () {
  417. $intervalSpy = jasmine.createSpy('$interval', $interval);
  418. module('toaster', function ($provide) {
  419. $provide.value('$interval', $intervalSpy);
  420. });
  421. // inject the toaster service
  422. inject(function (_toaster_, _$rootScope_, _$compile_) {
  423. toaster = _toaster_;
  424. rootScope = _$rootScope_;
  425. $compile = _$compile_;
  426. });
  427. });
  428. it('should use the toast.timeout argument if it is a valid number', function () {
  429. var container = compileContainer();
  430. var scope = container.scope();
  431. spyOn(scope, 'configureTimer').and.callThrough();
  432. toaster.pop({ timeout: 2 });
  433. expect(scope.configureTimer).toHaveBeenCalled();
  434. expect(scope.configureTimer.calls.allArgs()[0][0].timeout).toBe(2);
  435. expect($intervalSpy.calls.first().args[1]).toBe(2)
  436. });
  437. it('should not use the toast.timeout argument if not a valid number', function () {
  438. var container = compileContainer();
  439. var scope = container.scope();
  440. spyOn(scope, 'configureTimer').and.callThrough();
  441. toaster.pop({ timeout: "2" });
  442. expect(scope.configureTimer).toHaveBeenCalled();
  443. expect(scope.configureTimer.calls.allArgs()[0][0].timeout).toBe("2");
  444. expect($intervalSpy.calls.first().args[1]).toBe(5000);
  445. });
  446. it('should call scope.removeToast when toast.timeoutPromise expires', function () {
  447. var container = compileContainer();
  448. var scope = container.scope();
  449. spyOn(scope, 'removeToast').and.callThrough();
  450. toaster.pop({ timeout: 2 });
  451. $intervalSpy.calls.first().args[0]();
  452. rootScope.$digest();
  453. expect(scope.removeToast).toHaveBeenCalled();
  454. });
  455. it('should retrieve timeout by toast type if mergedConfig toast-timeout is an object', function () {
  456. var element = angular.element(
  457. '<toaster-container toaster-options="{\'time-out\': {\'toast-info\': 5}}"></toaster-container>');
  458. $compile(element)(rootScope);
  459. rootScope.$digest();
  460. toaster.pop({ type: 'info' });
  461. expect($intervalSpy.calls.first().args[1]).toBe(5);
  462. });
  463. it('should not set a timeout if mergedConfig toast-timeout is an object and does not match toast type', function () {
  464. // TODO: this seems to be a bug in the toast-timeout configuration option.
  465. // It should fall back to a default value if the toast type configuration
  466. // does not match the target toast type or throw an exception to warn of an
  467. // invalid configuration.
  468. var element = angular.element(
  469. '<toaster-container toaster-options="{\'time-out\': {\'toast-info\': 5}}"></toaster-container>');
  470. $compile(element)(rootScope);
  471. rootScope.$digest();
  472. toaster.pop({ type: 'warning' });
  473. expect($intervalSpy.calls.all().length).toBe(0);
  474. });
  475. });
  476. function compileContainer() {
  477. var element = angular.element('<toaster-container></toaster-container>');
  478. $compile(element)(rootScope);
  479. rootScope.$digest();
  480. return element;
  481. }