@angular-wave/angular.ts 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Makefile +1 -0
- package/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -12
- package/index.html +3 -74
- package/package.json +1 -1
- package/src/angular.spec.js +5 -0
- package/src/animations/animate-css.js +13 -5
- package/src/animations/animate-queue.js +21 -22
- package/src/animations/animate-runner.js +8 -4
- package/src/animations/animate.md +1 -1
- package/src/animations/animate.spec.js +21 -0
- package/src/animations/animation.js +1 -1
- package/src/binding.spec.js +1 -0
- package/src/core/compile/compile.js +25 -27
- package/src/core/compile/compile.spec.js +266 -17
- package/src/core/controller/controller.js +0 -2
- package/src/core/di/injector.md +1 -1
- package/src/core/di/injector.spec.js +2 -0
- package/src/core/di/internal-injector.js +1 -2
- package/src/core/interpolate/interpolate.js +12 -28
- package/src/core/interpolate/interpolate.spec.js +16 -70
- package/src/core/interval/interval-factory.js +50 -0
- package/src/core/interval/interval.html +18 -0
- package/src/core/interval/interval.js +77 -0
- package/src/core/interval/interval.md +123 -0
- package/src/core/interval/interval.spec.js +280 -0
- package/src/core/interval/interval.test.js +1 -1
- package/src/core/location/location.js +53 -59
- package/src/core/location/location.spec.js +27 -27
- package/src/core/on.spec.js +7 -0
- package/src/core/parse/interpreter.js +7 -10
- package/src/core/parse/parse.js +5 -26
- package/src/core/parse/parse.spec.js +91 -95
- package/src/core/prop.spec.js +60 -4
- package/src/core/q/q.html +18 -0
- package/src/core/q/q.js +472 -0
- package/src/core/q/q.md +211 -0
- package/src/core/q/q.spec.js +2748 -0
- package/src/core/q/q.test.js +12 -0
- package/src/core/sce/sce.spec.js +8 -0
- package/src/core/{model/model.html → scope/scope.html} +1 -1
- package/src/core/scope/scope.js +16 -15
- package/src/core/scope/scope.spec.js +1959 -24
- package/src/core/scope/scope.test.js +12 -0
- package/src/core/timeout/timeout.html +18 -0
- package/src/core/timeout/timeout.js +109 -0
- package/src/core/timeout/timeout.spec.js +354 -0
- package/src/core/timeout/timout.test.js +12 -0
- package/src/core/url-utils/url-utils.spec.js +1 -1
- package/src/directive/aria/aria.js +6 -3
- package/src/directive/aria/aria.spec.js +87 -0
- package/src/directive/attrs/attrs.spec.js +5 -0
- package/src/directive/attrs/boolean.spec.js +15 -0
- package/src/directive/attrs/element-style.spec.js +8 -0
- package/src/directive/attrs/src.spec.js +7 -0
- package/src/directive/bind/bind.spec.js +33 -0
- package/src/directive/bind/bing-html.spec.js +3 -0
- package/src/directive/class/class.js +3 -3
- package/src/directive/class/class.spec.js +75 -9
- package/src/directive/controller/controller.spec.js +13 -0
- package/src/directive/events/click.spec.js +3 -0
- package/src/directive/events/event.spec.js +6 -0
- package/src/directive/events/events.html +1 -0
- package/src/directive/form/form.js +3 -2
- package/src/directive/form/form.spec.js +65 -0
- package/src/directive/if/if.spec.js +4 -0
- package/src/directive/include/include.spec.js +59 -8
- package/src/directive/init/init.js +2 -6
- package/src/directive/init/init.spec.js +2 -0
- package/src/directive/input/input.spec.js +136 -0
- package/src/directive/messages/messages.spec.js +35 -4
- package/src/directive/model/model.js +25 -18
- package/src/directive/model/model.spec.js +49 -2
- package/src/directive/model-options/model-options.spec.js +6 -0
- package/src/directive/non-bindable/non-bindable.spec.js +1 -0
- package/src/directive/observe/observe.js +5 -1
- package/src/directive/observe/observe.spec.js +22 -0
- package/src/directive/observe/test.html +3 -11
- package/src/directive/options/options.spec.js +34 -0
- package/src/directive/ref/href.spec.js +15 -0
- package/src/directive/repeat/repeat.spec.js +135 -8
- package/src/directive/script/script.spec.js +2 -0
- package/src/directive/select/select.js +3 -3
- package/src/directive/select/select.spec.js +96 -0
- package/src/directive/show-hide/show-hide.js +2 -2
- package/src/directive/show-hide/show-hide.spec.js +19 -8
- package/src/directive/style/style.spec.js +7 -0
- package/src/directive/switch/switch.spec.js +5 -5
- package/src/directive/validators/validators.spec.js +1 -0
- package/src/loader.js +1 -0
- package/src/public.js +10 -2
- package/src/router/common/coreservices.js +2 -0
- package/src/router/directives/state-directives.js +14 -6
- package/src/router/directives/state-directives.spec.js +83 -0
- package/src/router/directives/view-directive.js +13 -4
- package/src/router/directives/view-directive.spec.js +71 -25
- package/src/router/hooks/lazy-load.js +2 -2
- package/src/router/hooks/views.js +5 -3
- package/src/router/resolve/resolvable.js +6 -3
- package/src/router/resolve/resolve-context.js +2 -2
- package/src/router/state/state-service.js +4 -4
- package/src/router/state/state.spec.js +5 -2
- package/src/router/state/state.test.js +1 -1
- package/src/router/state/views.js +10 -7
- package/src/router/template-factory.js +6 -3
- package/src/router/template-factory.spec.js +4 -0
- package/src/router/transition/transition-hook.js +1 -1
- package/src/router/transition/transition.js +1 -1
- package/src/router/view-hook.spec.js +2 -2
- package/src/router/view-scroll.js +6 -4
- package/src/services/browser.js +5 -8
- package/src/services/http/http.js +9 -6
- package/src/services/http/http.spec.js +31 -30
- package/src/services/http/template-request.spec.js +10 -0
- package/src/services/http-backend/http-backend.spec.js +3 -3
- package/src/services/template-request.js +4 -2
- package/src/shared/common.js +2 -1
- package/types/core/location/location.d.ts +37 -34
- package/types/core/parse/parse.d.ts +0 -26
- package/types/core/scope/scope.d.ts +11 -11
- package/src/core/model/model.js +0 -944
- package/src/core/model/model.spec.js +0 -3012
- package/types/core/model/model.d.ts +0 -204
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $$asyncQueue, Scope
|
|
1
|
+
import { $$asyncQueue, Scope } from "./scope";
|
|
2
2
|
import { extend, sliceArgs } from "../../shared/utils";
|
|
3
3
|
import { Angular } from "../../loader";
|
|
4
4
|
import { createInjector } from "../di/injector";
|
|
@@ -9,7 +9,6 @@ describe("Scope", function () {
|
|
|
9
9
|
let $browser;
|
|
10
10
|
let logs;
|
|
11
11
|
let scope;
|
|
12
|
-
let injector;
|
|
13
12
|
|
|
14
13
|
beforeEach(() => {
|
|
15
14
|
logs = [];
|
|
@@ -24,7 +23,7 @@ describe("Scope", function () {
|
|
|
24
23
|
};
|
|
25
24
|
});
|
|
26
25
|
|
|
27
|
-
injector = createInjector(["myModule"]);
|
|
26
|
+
let injector = createInjector(["myModule"]);
|
|
28
27
|
$parse = injector.get("$parse");
|
|
29
28
|
$browser = injector.get("$browser");
|
|
30
29
|
|
|
@@ -197,6 +196,822 @@ describe("Scope", function () {
|
|
|
197
196
|
});
|
|
198
197
|
|
|
199
198
|
describe("$watch/$digest", () => {
|
|
199
|
+
it("calls the listener function of a watch on first $digest", function () {
|
|
200
|
+
var watchFn = function () {
|
|
201
|
+
return "wat";
|
|
202
|
+
};
|
|
203
|
+
var listenerFn = jasmine.createSpy();
|
|
204
|
+
$rootScope.$watch(watchFn, listenerFn);
|
|
205
|
+
$rootScope.$digest();
|
|
206
|
+
expect(listenerFn).toHaveBeenCalled();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("calls the watch function with the scope as the argument", function () {
|
|
210
|
+
var watchFn = jasmine.createSpy();
|
|
211
|
+
var listenerFn = function () {};
|
|
212
|
+
$rootScope.$watch(watchFn, listenerFn);
|
|
213
|
+
$rootScope.$digest();
|
|
214
|
+
expect(watchFn).toHaveBeenCalledWith($rootScope);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it("calls the listener function when the watched value changes", function () {
|
|
218
|
+
scope.someValue = "a";
|
|
219
|
+
scope.counter = 0;
|
|
220
|
+
scope.$watch(
|
|
221
|
+
function (scope) {
|
|
222
|
+
return scope.someValue;
|
|
223
|
+
},
|
|
224
|
+
function (newValue, oldValue, scope) {
|
|
225
|
+
scope.counter++;
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
expect(scope.counter).toBe(0);
|
|
229
|
+
scope.$digest();
|
|
230
|
+
expect(scope.counter).toBe(1);
|
|
231
|
+
scope.$digest();
|
|
232
|
+
expect(scope.counter).toBe(1);
|
|
233
|
+
scope.someValue = "b";
|
|
234
|
+
expect(scope.counter).toBe(1);
|
|
235
|
+
scope.$digest();
|
|
236
|
+
expect(scope.counter).toBe(2);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("should watch and fire on simple property change", () => {
|
|
240
|
+
const spy = jasmine.createSpy();
|
|
241
|
+
$rootScope.$watch("name", spy);
|
|
242
|
+
$rootScope.$digest();
|
|
243
|
+
spy.calls.reset();
|
|
244
|
+
|
|
245
|
+
expect(spy).not.toHaveBeenCalled();
|
|
246
|
+
$rootScope.$digest();
|
|
247
|
+
expect(spy).not.toHaveBeenCalled();
|
|
248
|
+
$rootScope.name = "misko";
|
|
249
|
+
$rootScope.$digest();
|
|
250
|
+
expect(spy).toHaveBeenCalledWith("misko", undefined, $rootScope);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should not expose the `inner working of watch", () => {
|
|
254
|
+
function Getter() {
|
|
255
|
+
expect(this).toBeUndefined();
|
|
256
|
+
return "foo";
|
|
257
|
+
}
|
|
258
|
+
function Listener() {
|
|
259
|
+
expect(this).toBeUndefined();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
$rootScope.$watch(Getter, Listener);
|
|
263
|
+
$rootScope.$digest();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("should watch and fire on expression change", () => {
|
|
267
|
+
const spy = jasmine.createSpy();
|
|
268
|
+
$rootScope.$watch("name.first", spy);
|
|
269
|
+
$rootScope.$digest();
|
|
270
|
+
spy.calls.reset();
|
|
271
|
+
|
|
272
|
+
$rootScope.name = {};
|
|
273
|
+
expect(spy).not.toHaveBeenCalled();
|
|
274
|
+
$rootScope.$digest();
|
|
275
|
+
expect(spy).not.toHaveBeenCalled();
|
|
276
|
+
$rootScope.name.first = "misko";
|
|
277
|
+
$rootScope.$digest();
|
|
278
|
+
expect(spy).toHaveBeenCalled();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("should decrement the watcherCount when destroying a child scope", () => {
|
|
282
|
+
const child1 = $rootScope.$new();
|
|
283
|
+
const child2 = $rootScope.$new();
|
|
284
|
+
const grandChild1 = child1.$new();
|
|
285
|
+
const grandChild2 = child2.$new();
|
|
286
|
+
child1.$watch("a", () => {});
|
|
287
|
+
child2.$watch("a", () => {});
|
|
288
|
+
grandChild1.$watch("a", () => {});
|
|
289
|
+
grandChild2.$watch("a", () => {});
|
|
290
|
+
|
|
291
|
+
expect($rootScope.$$watchersCount).toBe(4);
|
|
292
|
+
expect(child1.$$watchersCount).toBe(2);
|
|
293
|
+
expect(child2.$$watchersCount).toBe(2);
|
|
294
|
+
expect(grandChild1.$$watchersCount).toBe(1);
|
|
295
|
+
expect(grandChild2.$$watchersCount).toBe(1);
|
|
296
|
+
|
|
297
|
+
grandChild2.$destroy();
|
|
298
|
+
expect(child2.$$watchersCount).toBe(1);
|
|
299
|
+
expect($rootScope.$$watchersCount).toBe(3);
|
|
300
|
+
child1.$destroy();
|
|
301
|
+
expect($rootScope.$$watchersCount).toBe(1);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("should decrement the watcherCount when calling the remove function", () => {
|
|
305
|
+
const child1 = $rootScope.$new();
|
|
306
|
+
const child2 = $rootScope.$new();
|
|
307
|
+
const grandChild1 = child1.$new();
|
|
308
|
+
const grandChild2 = child2.$new();
|
|
309
|
+
let remove1;
|
|
310
|
+
let remove2;
|
|
311
|
+
|
|
312
|
+
remove1 = child1.$watch("a", () => {});
|
|
313
|
+
child2.$watch("a", () => {});
|
|
314
|
+
grandChild1.$watch("a", () => {});
|
|
315
|
+
remove2 = grandChild2.$watch("a", () => {});
|
|
316
|
+
|
|
317
|
+
remove2();
|
|
318
|
+
expect(grandChild2.$$watchersCount).toBe(0);
|
|
319
|
+
expect(child2.$$watchersCount).toBe(1);
|
|
320
|
+
expect($rootScope.$$watchersCount).toBe(3);
|
|
321
|
+
remove1();
|
|
322
|
+
expect(grandChild1.$$watchersCount).toBe(1);
|
|
323
|
+
expect(child1.$$watchersCount).toBe(1);
|
|
324
|
+
expect($rootScope.$$watchersCount).toBe(2);
|
|
325
|
+
|
|
326
|
+
// Execute everything a second time to be sure that calling the remove function
|
|
327
|
+
// several times, it only decrements the counter once
|
|
328
|
+
remove2();
|
|
329
|
+
expect(child2.$$watchersCount).toBe(1);
|
|
330
|
+
expect($rootScope.$$watchersCount).toBe(2);
|
|
331
|
+
remove1();
|
|
332
|
+
expect(child1.$$watchersCount).toBe(1);
|
|
333
|
+
expect($rootScope.$$watchersCount).toBe(2);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
describe("constants cleanup", () => {
|
|
337
|
+
beforeEach(() => (logs = []));
|
|
338
|
+
it("should remove $watch of constant literals after initial digest", () => {
|
|
339
|
+
$rootScope.$watch("[]", () => {});
|
|
340
|
+
$rootScope.$watch("{}", () => {});
|
|
341
|
+
$rootScope.$watch("1", () => {});
|
|
342
|
+
$rootScope.$watch('"foo"', () => {});
|
|
343
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
344
|
+
$rootScope.$digest();
|
|
345
|
+
|
|
346
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should remove $watchCollection of constant literals after initial digest", () => {
|
|
350
|
+
$rootScope.$watchCollection("[]", () => {});
|
|
351
|
+
$rootScope.$watchCollection("{}", () => {});
|
|
352
|
+
$rootScope.$watchCollection("1", () => {});
|
|
353
|
+
$rootScope.$watchCollection('"foo"', () => {});
|
|
354
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
355
|
+
$rootScope.$digest();
|
|
356
|
+
|
|
357
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("should remove $watchGroup of constant literals after initial digest", () => {
|
|
361
|
+
$rootScope.$watchGroup(["[]", "{}", "1", '"foo"'], () => {});
|
|
362
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
363
|
+
$rootScope.$digest();
|
|
364
|
+
|
|
365
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("should remove $watch of filtered constant literals after initial digest", () => {
|
|
369
|
+
$rootScope.$watch('[1] | filter:"x"', () => {});
|
|
370
|
+
$rootScope.$watch("1 | limitTo:2", () => {});
|
|
371
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
372
|
+
$rootScope.$digest();
|
|
373
|
+
|
|
374
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it("should remove $watchCollection of filtered constant literals after initial digest", () => {
|
|
378
|
+
$rootScope.$watchCollection('[1] | filter:"x"', () => {});
|
|
379
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
380
|
+
$rootScope.$digest();
|
|
381
|
+
|
|
382
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("should remove $watchGroup of filtered constant literals after initial digest", () => {
|
|
386
|
+
$rootScope.$watchGroup(['[1] | filter:"x"', "1 | limitTo:2"], () => {});
|
|
387
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
388
|
+
$rootScope.$digest();
|
|
389
|
+
|
|
390
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("should remove $watch of constant expressions after initial digest", () => {
|
|
394
|
+
$rootScope.$watch("1 + 1", () => {});
|
|
395
|
+
$rootScope.$watch('"a" + "b"', () => {});
|
|
396
|
+
$rootScope.$watch('"ab".length', () => {});
|
|
397
|
+
$rootScope.$watch("[].length", () => {});
|
|
398
|
+
$rootScope.$watch("(1 + 1) | limitTo:2", () => {});
|
|
399
|
+
expect($rootScope.$$watchers.length).not.toEqual(0);
|
|
400
|
+
$rootScope.$digest();
|
|
401
|
+
|
|
402
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
describe("onetime cleanup", () => {
|
|
407
|
+
it("should clean up stable watches on the watch queue", () => {
|
|
408
|
+
$rootScope.$watch("::foo", () => {});
|
|
409
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
410
|
+
$rootScope.$digest();
|
|
411
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
412
|
+
|
|
413
|
+
$rootScope.foo = "foo";
|
|
414
|
+
$rootScope.$digest();
|
|
415
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("should clean up stable watches from $watchCollection", () => {
|
|
419
|
+
$rootScope.$watchCollection("::foo", () => {});
|
|
420
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
421
|
+
|
|
422
|
+
$rootScope.$digest();
|
|
423
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
424
|
+
|
|
425
|
+
$rootScope.foo = [];
|
|
426
|
+
$rootScope.$digest();
|
|
427
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("should clean up stable watches from $watchCollection literals", () => {
|
|
431
|
+
$rootScope.$watchCollection("::[foo, bar]", () => {});
|
|
432
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
433
|
+
|
|
434
|
+
$rootScope.$digest();
|
|
435
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
436
|
+
|
|
437
|
+
$rootScope.foo = 1;
|
|
438
|
+
$rootScope.$digest();
|
|
439
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
440
|
+
|
|
441
|
+
$rootScope.foo = 2;
|
|
442
|
+
$rootScope.$digest();
|
|
443
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
444
|
+
|
|
445
|
+
$rootScope.bar = 3;
|
|
446
|
+
$rootScope.$digest();
|
|
447
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("should clean up stable watches from $watchGroup", () => {
|
|
451
|
+
$rootScope.$watchGroup(["::foo", "::bar"], () => {});
|
|
452
|
+
expect($rootScope.$$watchers.length).toEqual(2);
|
|
453
|
+
|
|
454
|
+
$rootScope.$digest();
|
|
455
|
+
expect($rootScope.$$watchers.length).toEqual(2);
|
|
456
|
+
|
|
457
|
+
$rootScope.foo = "foo";
|
|
458
|
+
$rootScope.$digest();
|
|
459
|
+
expect($rootScope.$$watchers.length).toEqual(1);
|
|
460
|
+
|
|
461
|
+
$rootScope.bar = "bar";
|
|
462
|
+
$rootScope.$digest();
|
|
463
|
+
expect($rootScope.$$watchers.length).toEqual(0);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("should delegate exceptions", () => {
|
|
468
|
+
$rootScope.$watch("a", () => {
|
|
469
|
+
throw new Error("abc");
|
|
470
|
+
});
|
|
471
|
+
$rootScope.a = 1;
|
|
472
|
+
$rootScope.$digest();
|
|
473
|
+
expect(logs[0]).toMatch(/abc/);
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it("should fire watches in order of addition", () => {
|
|
477
|
+
// this is not an external guarantee, just our own sanity
|
|
478
|
+
logs = "";
|
|
479
|
+
$rootScope.$watch("a", () => {
|
|
480
|
+
logs += "a";
|
|
481
|
+
});
|
|
482
|
+
$rootScope.$watch("b", () => {
|
|
483
|
+
logs += "b";
|
|
484
|
+
});
|
|
485
|
+
// constant expressions have slightly different handling,
|
|
486
|
+
// let's ensure they are kept in the same list as others
|
|
487
|
+
$rootScope.$watch("1", () => {
|
|
488
|
+
logs += "1";
|
|
489
|
+
});
|
|
490
|
+
$rootScope.$watch("c", () => {
|
|
491
|
+
logs += "c";
|
|
492
|
+
});
|
|
493
|
+
$rootScope.$watch("2", () => {
|
|
494
|
+
logs += "2";
|
|
495
|
+
});
|
|
496
|
+
$rootScope.a = $rootScope.b = $rootScope.c = 1;
|
|
497
|
+
$rootScope.$digest();
|
|
498
|
+
expect(logs).toEqual("ab1c2");
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it("should call child $watchers in addition order", () => {
|
|
502
|
+
// this is not an external guarantee, just our own sanity
|
|
503
|
+
logs = "";
|
|
504
|
+
const childA = $rootScope.$new();
|
|
505
|
+
const childB = $rootScope.$new();
|
|
506
|
+
const childC = $rootScope.$new();
|
|
507
|
+
childA.$watch("a", () => {
|
|
508
|
+
logs += "a";
|
|
509
|
+
});
|
|
510
|
+
childB.$watch("b", () => {
|
|
511
|
+
logs += "b";
|
|
512
|
+
});
|
|
513
|
+
childC.$watch("c", () => {
|
|
514
|
+
logs += "c";
|
|
515
|
+
});
|
|
516
|
+
childA.a = childB.b = childC.c = 1;
|
|
517
|
+
$rootScope.$digest();
|
|
518
|
+
expect(logs).toEqual("abc");
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it("should allow $digest on a child scope with and without a right sibling", () => {
|
|
522
|
+
// tests a traversal edge case which we originally missed
|
|
523
|
+
logs = "";
|
|
524
|
+
const childA = $rootScope.$new();
|
|
525
|
+
const childB = $rootScope.$new();
|
|
526
|
+
|
|
527
|
+
$rootScope.$watch(() => {
|
|
528
|
+
logs += "r";
|
|
529
|
+
});
|
|
530
|
+
childA.$watch(() => {
|
|
531
|
+
logs += "a";
|
|
532
|
+
});
|
|
533
|
+
childB.$watch(() => {
|
|
534
|
+
logs += "b";
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// init
|
|
538
|
+
$rootScope.$digest();
|
|
539
|
+
expect(logs).toBe("rabrab");
|
|
540
|
+
|
|
541
|
+
logs = "";
|
|
542
|
+
childA.$digest();
|
|
543
|
+
expect(logs).toBe("a");
|
|
544
|
+
|
|
545
|
+
logs = "";
|
|
546
|
+
childB.$digest();
|
|
547
|
+
expect(logs).toBe("b");
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it("should repeat watch cycle while model changes are identified", () => {
|
|
551
|
+
logs = "";
|
|
552
|
+
$rootScope.$watch("c", (v) => {
|
|
553
|
+
$rootScope.d = v;
|
|
554
|
+
logs += "c";
|
|
555
|
+
});
|
|
556
|
+
$rootScope.$watch("b", (v) => {
|
|
557
|
+
$rootScope.c = v;
|
|
558
|
+
logs += "b";
|
|
559
|
+
});
|
|
560
|
+
$rootScope.$watch("a", (v) => {
|
|
561
|
+
$rootScope.b = v;
|
|
562
|
+
logs += "a";
|
|
563
|
+
});
|
|
564
|
+
$rootScope.$digest();
|
|
565
|
+
logs = "";
|
|
566
|
+
$rootScope.a = 1;
|
|
567
|
+
$rootScope.$digest();
|
|
568
|
+
expect($rootScope.b).toEqual(1);
|
|
569
|
+
expect($rootScope.c).toEqual(1);
|
|
570
|
+
expect($rootScope.d).toEqual(1);
|
|
571
|
+
expect(logs).toEqual("abc");
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it("should repeat watch cycle from the root element", () => {
|
|
575
|
+
logs = "";
|
|
576
|
+
const child = $rootScope.$new();
|
|
577
|
+
$rootScope.$watch(() => {
|
|
578
|
+
logs += "a";
|
|
579
|
+
});
|
|
580
|
+
child.$watch(() => {
|
|
581
|
+
logs += "b";
|
|
582
|
+
});
|
|
583
|
+
$rootScope.$digest();
|
|
584
|
+
expect(logs).toEqual("abab");
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it("should prevent infinite recursion and print watcher expression", () => {
|
|
588
|
+
$rootScope.$watch("a", function () {
|
|
589
|
+
$rootScope.b++;
|
|
590
|
+
});
|
|
591
|
+
$rootScope.$watch("b", function () {
|
|
592
|
+
$rootScope.a++;
|
|
593
|
+
});
|
|
594
|
+
$rootScope.a = $rootScope.b = 0;
|
|
595
|
+
expect(function () {
|
|
596
|
+
$rootScope.$digest();
|
|
597
|
+
}).toThrow();
|
|
598
|
+
|
|
599
|
+
expect($rootScope.$$phase).toBe(0);
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it("should prevent infinite recursion and print watcher function name or body", () => {
|
|
603
|
+
$rootScope.$watch(
|
|
604
|
+
() => $rootScope.a,
|
|
605
|
+
() => {
|
|
606
|
+
$rootScope.b++;
|
|
607
|
+
},
|
|
608
|
+
);
|
|
609
|
+
$rootScope.$watch(
|
|
610
|
+
() => $rootScope.b,
|
|
611
|
+
() => {
|
|
612
|
+
$rootScope.a++;
|
|
613
|
+
},
|
|
614
|
+
);
|
|
615
|
+
$rootScope.a = $rootScope.b = 0;
|
|
616
|
+
|
|
617
|
+
try {
|
|
618
|
+
$rootScope.$digest();
|
|
619
|
+
throw new Error("Should have thrown exception");
|
|
620
|
+
} catch (e) {
|
|
621
|
+
console.error(e);
|
|
622
|
+
expect(e.message.match(/rootScope.a/g).length).toBeTruthy();
|
|
623
|
+
expect(e.message.match(/rootScope.b/g).length).toBeTruthy();
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// it("should prevent infinite loop when creating and resolving a promise in a watched expression", () => {
|
|
628
|
+
// module(($rootScopeProvider) => {
|
|
629
|
+
// $rootScopeProvider.digestTtl(10);
|
|
630
|
+
// });
|
|
631
|
+
// () => {
|
|
632
|
+
// const d = $q.defer();
|
|
633
|
+
|
|
634
|
+
// d.resolve("Hello, world.");
|
|
635
|
+
// $rootScope.$watch(
|
|
636
|
+
// () => {
|
|
637
|
+
// const $d2 = $q.defer();
|
|
638
|
+
// $d2.resolve("Goodbye.");
|
|
639
|
+
// $d2.promise.then(() => {});
|
|
640
|
+
// return d.promise;
|
|
641
|
+
// },
|
|
642
|
+
// () => 0,
|
|
643
|
+
// );
|
|
644
|
+
|
|
645
|
+
// expect(() => {
|
|
646
|
+
// $rootScope.$digest();
|
|
647
|
+
// }).toThrow(
|
|
648
|
+
// "$rootScope",
|
|
649
|
+
// "infdig",
|
|
650
|
+
// "10 $digest() iterations reached. Aborting!\n" +
|
|
651
|
+
// "Watchers fired in the last 5 iterations: []",
|
|
652
|
+
// );
|
|
653
|
+
|
|
654
|
+
// expect($rootScope.$$phase).toBeNull();
|
|
655
|
+
// });
|
|
656
|
+
// });
|
|
657
|
+
|
|
658
|
+
it("should not fire upon $watch registration on initial $digest", () => {
|
|
659
|
+
logs = "";
|
|
660
|
+
$rootScope.a = 1;
|
|
661
|
+
$rootScope.$watch("a", () => {
|
|
662
|
+
logs += "a";
|
|
663
|
+
});
|
|
664
|
+
$rootScope.$watch("b", () => {
|
|
665
|
+
logs += "b";
|
|
666
|
+
});
|
|
667
|
+
$rootScope.$digest();
|
|
668
|
+
logs = "";
|
|
669
|
+
$rootScope.$digest();
|
|
670
|
+
expect(logs).toEqual("");
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
it("should watch objects", () => {
|
|
674
|
+
logs = "";
|
|
675
|
+
$rootScope.a = [];
|
|
676
|
+
$rootScope.b = {};
|
|
677
|
+
$rootScope.$watch(
|
|
678
|
+
"a",
|
|
679
|
+
(value) => {
|
|
680
|
+
logs += ".";
|
|
681
|
+
expect(value).toBe($rootScope.a);
|
|
682
|
+
},
|
|
683
|
+
true,
|
|
684
|
+
);
|
|
685
|
+
$rootScope.$watch(
|
|
686
|
+
"b",
|
|
687
|
+
(value) => {
|
|
688
|
+
logs += "!";
|
|
689
|
+
expect(value).toBe($rootScope.b);
|
|
690
|
+
},
|
|
691
|
+
true,
|
|
692
|
+
);
|
|
693
|
+
$rootScope.$digest();
|
|
694
|
+
logs = "";
|
|
695
|
+
|
|
696
|
+
$rootScope.a.push({});
|
|
697
|
+
$rootScope.b.name = "";
|
|
698
|
+
|
|
699
|
+
$rootScope.$digest();
|
|
700
|
+
expect(logs).toEqual(".!");
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it("should watch functions", () => {
|
|
704
|
+
$rootScope.fn = function () {
|
|
705
|
+
return "a";
|
|
706
|
+
};
|
|
707
|
+
$rootScope.$watch("fn", (fn) => {
|
|
708
|
+
logs.push(fn());
|
|
709
|
+
});
|
|
710
|
+
$rootScope.$digest();
|
|
711
|
+
expect(logs).toEqual(["a"]);
|
|
712
|
+
$rootScope.fn = function () {
|
|
713
|
+
return "b";
|
|
714
|
+
};
|
|
715
|
+
$rootScope.$digest();
|
|
716
|
+
expect(logs).toEqual(["a", "b"]);
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it("should prevent $digest recursion", () => {
|
|
720
|
+
let callCount = 0;
|
|
721
|
+
$rootScope.$watch("name", () => {
|
|
722
|
+
expect(() => {
|
|
723
|
+
$rootScope.$digest();
|
|
724
|
+
}).toThrowError(/digest already in progress/);
|
|
725
|
+
callCount++;
|
|
726
|
+
});
|
|
727
|
+
$rootScope.name = "a";
|
|
728
|
+
$rootScope.$digest();
|
|
729
|
+
expect(callCount).toEqual(1);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it("should allow a watch to be added while in a digest", () => {
|
|
733
|
+
const watch1 = jasmine.createSpy("watch1");
|
|
734
|
+
const watch2 = jasmine.createSpy("watch2");
|
|
735
|
+
$rootScope.$watch("foo", () => {
|
|
736
|
+
$rootScope.$watch("foo", watch1);
|
|
737
|
+
$rootScope.$watch("foo", watch2);
|
|
738
|
+
});
|
|
739
|
+
$rootScope.$apply("foo = true");
|
|
740
|
+
expect(watch1).toHaveBeenCalled();
|
|
741
|
+
expect(watch2).toHaveBeenCalled();
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it("should not skip watchers when adding new watchers during digest", () => {
|
|
745
|
+
const watchFn1 = function () {
|
|
746
|
+
logs.push(1);
|
|
747
|
+
};
|
|
748
|
+
const watchFn2 = function () {
|
|
749
|
+
logs.push(2);
|
|
750
|
+
};
|
|
751
|
+
const watchFn3 = function () {
|
|
752
|
+
logs.push(3);
|
|
753
|
+
};
|
|
754
|
+
const addWatcherOnce = function (newValue, oldValue) {
|
|
755
|
+
if (newValue === oldValue) {
|
|
756
|
+
$rootScope.$watch(watchFn3);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
$rootScope.$watch(watchFn1, addWatcherOnce);
|
|
761
|
+
$rootScope.$watch(watchFn2);
|
|
762
|
+
|
|
763
|
+
$rootScope.$digest();
|
|
764
|
+
|
|
765
|
+
expect(logs).toEqual([1, 2, 3, 1, 2, 3]);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
it("should not run the current watcher twice when removing a watcher during digest", () => {
|
|
769
|
+
let removeWatcher3;
|
|
770
|
+
|
|
771
|
+
const watchFn3 = function () {
|
|
772
|
+
logs.push(3);
|
|
773
|
+
};
|
|
774
|
+
const watchFn2 = function () {
|
|
775
|
+
logs.push(2);
|
|
776
|
+
};
|
|
777
|
+
const watchFn1 = function () {
|
|
778
|
+
logs.push(1);
|
|
779
|
+
};
|
|
780
|
+
const removeWatcherOnce = function (newValue, oldValue) {
|
|
781
|
+
if (newValue === oldValue) {
|
|
782
|
+
removeWatcher3();
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
$rootScope.$watch(watchFn1, removeWatcherOnce);
|
|
787
|
+
$rootScope.$watch(watchFn2);
|
|
788
|
+
removeWatcher3 = $rootScope.$watch(watchFn3);
|
|
789
|
+
|
|
790
|
+
$rootScope.$digest();
|
|
791
|
+
|
|
792
|
+
expect(logs).toEqual([1, 2, 1, 2]);
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
it("should not skip watchers when removing itself during digest", () => {
|
|
796
|
+
let removeWatcher1;
|
|
797
|
+
|
|
798
|
+
const watchFn3 = function () {
|
|
799
|
+
logs.push(3);
|
|
800
|
+
};
|
|
801
|
+
const watchFn2 = function () {
|
|
802
|
+
logs.push(2);
|
|
803
|
+
};
|
|
804
|
+
const watchFn1 = function () {
|
|
805
|
+
logs.push(1);
|
|
806
|
+
};
|
|
807
|
+
const removeItself = function () {
|
|
808
|
+
removeWatcher1();
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
removeWatcher1 = $rootScope.$watch(watchFn1, removeItself);
|
|
812
|
+
$rootScope.$watch(watchFn2);
|
|
813
|
+
$rootScope.$watch(watchFn3);
|
|
814
|
+
|
|
815
|
+
$rootScope.$digest();
|
|
816
|
+
|
|
817
|
+
expect(logs).toEqual([1, 2, 3, 2, 3]);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it("should not infinitely digest when current value is NaN", () => {
|
|
821
|
+
$rootScope.$watch(() => NaN);
|
|
822
|
+
|
|
823
|
+
expect(() => {
|
|
824
|
+
$rootScope.$digest();
|
|
825
|
+
}).not.toThrow();
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it("should always call the watcher with newVal and oldVal equal on the first run", () => {
|
|
829
|
+
function logger(scope, newVal, oldVal) {
|
|
830
|
+
const val =
|
|
831
|
+
newVal === oldVal || (newVal !== oldVal && oldVal !== newVal)
|
|
832
|
+
? newVal
|
|
833
|
+
: "xxx";
|
|
834
|
+
logs.push(val);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
$rootScope.$watch(() => NaN, logger);
|
|
838
|
+
$rootScope.$watch(() => undefined, logger);
|
|
839
|
+
$rootScope.$watch(() => "", logger);
|
|
840
|
+
$rootScope.$watch(() => false, logger);
|
|
841
|
+
$rootScope.$watch(() => ({}), logger, true);
|
|
842
|
+
$rootScope.$watch(() => 23, logger);
|
|
843
|
+
|
|
844
|
+
$rootScope.$digest();
|
|
845
|
+
expect(isNaN(logs.shift())).toBe(true); // jasmine's toBe and toEqual don't work well with NaNs
|
|
846
|
+
expect(logs).toEqual([undefined, "", false, {}, 23]);
|
|
847
|
+
logs = [];
|
|
848
|
+
$rootScope.$digest();
|
|
849
|
+
expect(logs).toEqual([]);
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
describe("$watch deregistration", () => {
|
|
853
|
+
beforeEach(() => (logs = []));
|
|
854
|
+
it("should return a function that allows listeners to be deregistered", () => {
|
|
855
|
+
const listener = jasmine.createSpy("watch listener");
|
|
856
|
+
let listenerRemove;
|
|
857
|
+
|
|
858
|
+
listenerRemove = $rootScope.$watch("foo", listener);
|
|
859
|
+
$rootScope.$digest(); // init
|
|
860
|
+
expect(listener).toHaveBeenCalled();
|
|
861
|
+
expect(listenerRemove).toBeDefined();
|
|
862
|
+
|
|
863
|
+
listener.calls.reset();
|
|
864
|
+
$rootScope.foo = "bar";
|
|
865
|
+
$rootScope.$digest(); // trigger
|
|
866
|
+
expect(listener).toHaveBeenCalled();
|
|
867
|
+
|
|
868
|
+
listener.calls.reset();
|
|
869
|
+
$rootScope.foo = "baz";
|
|
870
|
+
listenerRemove();
|
|
871
|
+
$rootScope.$digest(); // trigger
|
|
872
|
+
expect(listener).not.toHaveBeenCalled();
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("should allow a watch to be deregistered while in a digest", () => {
|
|
876
|
+
let remove1;
|
|
877
|
+
let remove2;
|
|
878
|
+
$rootScope.$watch("remove", () => {
|
|
879
|
+
remove1();
|
|
880
|
+
remove2();
|
|
881
|
+
});
|
|
882
|
+
remove1 = $rootScope.$watch("thing", () => {});
|
|
883
|
+
remove2 = $rootScope.$watch("thing", () => {});
|
|
884
|
+
expect(() => {
|
|
885
|
+
$rootScope.$apply("remove = true");
|
|
886
|
+
}).not.toThrow();
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
it("should not mess up the digest loop if deregistration happens during digest", () => {
|
|
890
|
+
// we are testing this due to regression #5525 which is related to how the digest loops lastDirtyWatch short-circuiting optimization works
|
|
891
|
+
// scenario: watch1 deregistering watch1
|
|
892
|
+
let scope = $rootScope.$new();
|
|
893
|
+
let deregWatch1 = scope.$watch(
|
|
894
|
+
() => {
|
|
895
|
+
logs.push("watch1");
|
|
896
|
+
return "watch1";
|
|
897
|
+
},
|
|
898
|
+
() => {
|
|
899
|
+
deregWatch1();
|
|
900
|
+
logs.push("watchAction1");
|
|
901
|
+
},
|
|
902
|
+
);
|
|
903
|
+
scope.$watch(
|
|
904
|
+
() => {
|
|
905
|
+
logs.push("watch2");
|
|
906
|
+
return "watch2";
|
|
907
|
+
},
|
|
908
|
+
() => logs.push("watchAction2"),
|
|
909
|
+
);
|
|
910
|
+
scope.$watch(
|
|
911
|
+
() => {
|
|
912
|
+
logs.push("watch3");
|
|
913
|
+
return "watch3";
|
|
914
|
+
},
|
|
915
|
+
() => logs.push("watchAction3"),
|
|
916
|
+
);
|
|
917
|
+
|
|
918
|
+
$rootScope.$digest();
|
|
919
|
+
|
|
920
|
+
expect(logs).toEqual([
|
|
921
|
+
"watch1",
|
|
922
|
+
"watchAction1",
|
|
923
|
+
"watch2",
|
|
924
|
+
"watchAction2",
|
|
925
|
+
"watch3",
|
|
926
|
+
"watchAction3",
|
|
927
|
+
"watch2",
|
|
928
|
+
"watch3",
|
|
929
|
+
]);
|
|
930
|
+
scope.$destroy();
|
|
931
|
+
logs = [];
|
|
932
|
+
|
|
933
|
+
// scenario: watch1 deregistering watch2
|
|
934
|
+
scope = $rootScope.$new();
|
|
935
|
+
scope.$watch(
|
|
936
|
+
() => {
|
|
937
|
+
logs.push("watch1");
|
|
938
|
+
return "watch1";
|
|
939
|
+
},
|
|
940
|
+
() => {
|
|
941
|
+
deregWatch2();
|
|
942
|
+
logs.push("watchAction1");
|
|
943
|
+
},
|
|
944
|
+
);
|
|
945
|
+
let deregWatch2 = scope.$watch(
|
|
946
|
+
() => {
|
|
947
|
+
logs.push("watch2");
|
|
948
|
+
return "watch2";
|
|
949
|
+
},
|
|
950
|
+
() => logs.push("watchAction2"),
|
|
951
|
+
);
|
|
952
|
+
scope.$watch(
|
|
953
|
+
() => {
|
|
954
|
+
logs.push("watch3");
|
|
955
|
+
return "watch3";
|
|
956
|
+
},
|
|
957
|
+
() => logs.push("watchAction3"),
|
|
958
|
+
);
|
|
959
|
+
|
|
960
|
+
$rootScope.$digest();
|
|
961
|
+
|
|
962
|
+
expect(logs).toEqual([
|
|
963
|
+
"watch1",
|
|
964
|
+
"watchAction1",
|
|
965
|
+
"watch3",
|
|
966
|
+
"watchAction3",
|
|
967
|
+
"watch1",
|
|
968
|
+
"watch3",
|
|
969
|
+
]);
|
|
970
|
+
scope.$destroy();
|
|
971
|
+
logs = [];
|
|
972
|
+
|
|
973
|
+
// scenario: watch2 deregistering watch1
|
|
974
|
+
scope = $rootScope.$new();
|
|
975
|
+
deregWatch1 = scope.$watch(
|
|
976
|
+
() => {
|
|
977
|
+
logs.push("watch1");
|
|
978
|
+
return "watch1";
|
|
979
|
+
},
|
|
980
|
+
() => logs.push("watchAction1"),
|
|
981
|
+
);
|
|
982
|
+
scope.$watch(
|
|
983
|
+
() => {
|
|
984
|
+
logs.push("watch2");
|
|
985
|
+
return "watch2";
|
|
986
|
+
},
|
|
987
|
+
() => {
|
|
988
|
+
deregWatch1();
|
|
989
|
+
logs.push("watchAction2");
|
|
990
|
+
},
|
|
991
|
+
);
|
|
992
|
+
scope.$watch(
|
|
993
|
+
() => {
|
|
994
|
+
logs.push("watch3");
|
|
995
|
+
return "watch3";
|
|
996
|
+
},
|
|
997
|
+
() => logs.push("watchAction3"),
|
|
998
|
+
);
|
|
999
|
+
|
|
1000
|
+
$rootScope.$digest();
|
|
1001
|
+
|
|
1002
|
+
expect(logs).toEqual([
|
|
1003
|
+
"watch1",
|
|
1004
|
+
"watchAction1",
|
|
1005
|
+
"watch2",
|
|
1006
|
+
"watchAction2",
|
|
1007
|
+
"watch3",
|
|
1008
|
+
"watchAction3",
|
|
1009
|
+
"watch2",
|
|
1010
|
+
"watch3",
|
|
1011
|
+
]);
|
|
1012
|
+
});
|
|
1013
|
+
});
|
|
1014
|
+
|
|
200
1015
|
describe("$watchCollection", () => {
|
|
201
1016
|
describe("constiable", () => {
|
|
202
1017
|
let deregister;
|
|
@@ -214,21 +1029,25 @@ describe("Scope", function () {
|
|
|
214
1029
|
});
|
|
215
1030
|
|
|
216
1031
|
it("should not trigger if nothing change", () => {
|
|
1032
|
+
$rootScope.$digest();
|
|
217
1033
|
expect(logs).toEqual([
|
|
218
1034
|
{ newVal: undefined, oldVal: undefined, identical: true },
|
|
219
1035
|
]);
|
|
220
1036
|
logs = [];
|
|
1037
|
+
$rootScope.$digest();
|
|
221
1038
|
expect(logs).toEqual([]);
|
|
222
1039
|
});
|
|
223
1040
|
|
|
224
1041
|
it("should allow deregistration", () => {
|
|
225
1042
|
$rootScope.obj = [];
|
|
1043
|
+
$rootScope.$digest();
|
|
226
1044
|
expect(logs.length).toBe(1);
|
|
227
1045
|
logs = [];
|
|
228
1046
|
|
|
229
1047
|
$rootScope.obj.push("a");
|
|
230
1048
|
deregister();
|
|
231
1049
|
|
|
1050
|
+
$rootScope.$digest();
|
|
232
1051
|
expect(logs).toEqual([]);
|
|
233
1052
|
});
|
|
234
1053
|
|
|
@@ -236,6 +1055,7 @@ describe("Scope", function () {
|
|
|
236
1055
|
it("should return oldCollection === newCollection only on the first listener call", () => {
|
|
237
1056
|
// first time should be identical
|
|
238
1057
|
$rootScope.obj = ["a", "b"];
|
|
1058
|
+
$rootScope.$digest();
|
|
239
1059
|
expect(logs).toEqual([
|
|
240
1060
|
{ newVal: ["a", "b"], oldVal: ["a", "b"], identical: true },
|
|
241
1061
|
]);
|
|
@@ -243,58 +1063,99 @@ describe("Scope", function () {
|
|
|
243
1063
|
|
|
244
1064
|
// second time should be different
|
|
245
1065
|
$rootScope.obj[1] = "c";
|
|
1066
|
+
$rootScope.$digest();
|
|
246
1067
|
expect(logs).toEqual([{ newVal: ["a", "c"], oldVal: ["a", "b"] }]);
|
|
247
1068
|
});
|
|
248
1069
|
|
|
1070
|
+
it("should trigger when property changes into array", () => {
|
|
1071
|
+
$rootScope.obj = "test";
|
|
1072
|
+
$rootScope.$digest();
|
|
1073
|
+
expect(logs).toEqual([
|
|
1074
|
+
{ newVal: "test", oldVal: "test", identical: true },
|
|
1075
|
+
]);
|
|
1076
|
+
|
|
1077
|
+
logs = [];
|
|
1078
|
+
$rootScope.obj = [];
|
|
1079
|
+
$rootScope.$digest();
|
|
1080
|
+
expect(logs).toEqual([{ newVal: [], oldVal: "test" }]);
|
|
1081
|
+
|
|
1082
|
+
logs = [];
|
|
1083
|
+
$rootScope.obj = {};
|
|
1084
|
+
$rootScope.$digest();
|
|
1085
|
+
expect(logs).toEqual([{ newVal: {}, oldVal: [] }]);
|
|
1086
|
+
|
|
1087
|
+
logs = [];
|
|
1088
|
+
$rootScope.obj = [];
|
|
1089
|
+
$rootScope.$digest();
|
|
1090
|
+
expect(logs).toEqual([{ newVal: [], oldVal: {} }]);
|
|
1091
|
+
|
|
1092
|
+
logs = [];
|
|
1093
|
+
$rootScope.obj = undefined;
|
|
1094
|
+
$rootScope.$digest();
|
|
1095
|
+
expect(logs).toEqual([{ newVal: undefined, oldVal: [] }]);
|
|
1096
|
+
});
|
|
1097
|
+
|
|
249
1098
|
it("should not trigger change when object in collection changes", () => {
|
|
250
1099
|
$rootScope.obj = [{}];
|
|
1100
|
+
$rootScope.$digest();
|
|
251
1101
|
expect(logs).toEqual([
|
|
252
1102
|
{ newVal: [{}], oldVal: [{}], identical: true },
|
|
253
1103
|
]);
|
|
254
1104
|
|
|
255
1105
|
logs = [];
|
|
256
1106
|
$rootScope.obj[0].name = "foo";
|
|
1107
|
+
$rootScope.$digest();
|
|
257
1108
|
expect(logs).toEqual([]);
|
|
258
1109
|
});
|
|
259
1110
|
|
|
260
1111
|
it("should watch array properties", () => {
|
|
261
1112
|
$rootScope.obj = [];
|
|
1113
|
+
$rootScope.$digest();
|
|
262
1114
|
expect(logs).toEqual([{ newVal: [], oldVal: [], identical: true }]);
|
|
263
1115
|
|
|
264
1116
|
logs = [];
|
|
265
1117
|
$rootScope.obj.push("a");
|
|
1118
|
+
$rootScope.$digest();
|
|
266
1119
|
expect(logs).toEqual([{ newVal: ["a"], oldVal: [] }]);
|
|
267
1120
|
|
|
268
1121
|
logs = [];
|
|
269
1122
|
$rootScope.obj[0] = "b";
|
|
1123
|
+
$rootScope.$digest();
|
|
270
1124
|
expect(logs).toEqual([{ newVal: ["b"], oldVal: ["a"] }]);
|
|
271
1125
|
|
|
272
1126
|
logs = [];
|
|
273
1127
|
$rootScope.obj.push([]);
|
|
274
1128
|
$rootScope.obj.push({});
|
|
1129
|
+
$rootScope.$digest();
|
|
275
1130
|
expect(logs).toEqual([{ newVal: ["b", [], {}], oldVal: ["b"] }]);
|
|
276
1131
|
|
|
277
1132
|
logs = [];
|
|
278
1133
|
const temp = $rootScope.obj[1];
|
|
279
1134
|
$rootScope.obj[1] = $rootScope.obj[2];
|
|
280
1135
|
$rootScope.obj[2] = temp;
|
|
1136
|
+
$rootScope.$digest();
|
|
281
1137
|
expect(logs).toEqual([
|
|
282
1138
|
{ newVal: ["b", {}, []], oldVal: ["b", [], {}] },
|
|
283
1139
|
]);
|
|
284
1140
|
|
|
285
1141
|
logs = [];
|
|
286
1142
|
$rootScope.obj.shift();
|
|
1143
|
+
$rootScope.$digest();
|
|
287
1144
|
expect(logs).toEqual([{ newVal: [{}, []], oldVal: ["b", {}, []] }]);
|
|
288
1145
|
});
|
|
289
1146
|
|
|
290
1147
|
it("should not infinitely digest when current value is NaN", () => {
|
|
291
1148
|
$rootScope.obj = [NaN];
|
|
292
|
-
expect(() => {
|
|
1149
|
+
expect(() => {
|
|
1150
|
+
$rootScope.$digest();
|
|
1151
|
+
}).not.toThrow();
|
|
293
1152
|
});
|
|
294
1153
|
|
|
295
1154
|
it("should watch array-like objects like arrays", () => {
|
|
296
1155
|
logs = [];
|
|
297
1156
|
$rootScope.obj = document.getElementsByTagName("src");
|
|
1157
|
+
$rootScope.$digest();
|
|
1158
|
+
|
|
298
1159
|
expect(logs.length).toBeTruthy();
|
|
299
1160
|
});
|
|
300
1161
|
});
|
|
@@ -303,6 +1164,7 @@ describe("Scope", function () {
|
|
|
303
1164
|
it("should return oldCollection === newCollection only on the first listener call", () => {
|
|
304
1165
|
$rootScope.obj = { a: "b" };
|
|
305
1166
|
// first time should be identical
|
|
1167
|
+
$rootScope.$digest();
|
|
306
1168
|
expect(logs).toEqual([
|
|
307
1169
|
{ newVal: { a: "b" }, oldVal: { a: "b" }, identical: true },
|
|
308
1170
|
]);
|
|
@@ -310,45 +1172,54 @@ describe("Scope", function () {
|
|
|
310
1172
|
|
|
311
1173
|
// second time not identical
|
|
312
1174
|
$rootScope.obj.a = "c";
|
|
1175
|
+
$rootScope.$digest();
|
|
313
1176
|
expect(logs).toEqual([{ newVal: { a: "c" }, oldVal: { a: "b" } }]);
|
|
314
1177
|
});
|
|
315
1178
|
|
|
316
1179
|
it("should trigger when property changes into object", () => {
|
|
317
1180
|
$rootScope.obj = "test";
|
|
1181
|
+
$rootScope.$digest();
|
|
318
1182
|
expect(logs).toEqual([
|
|
319
1183
|
{ newVal: "test", oldVal: "test", identical: true },
|
|
320
1184
|
]);
|
|
321
1185
|
logs = [];
|
|
322
1186
|
|
|
323
1187
|
$rootScope.obj = {};
|
|
1188
|
+
$rootScope.$digest();
|
|
324
1189
|
expect(logs).toEqual([{ newVal: {}, oldVal: "test" }]);
|
|
325
1190
|
});
|
|
326
1191
|
|
|
327
1192
|
it("should not trigger change when object in collection changes", () => {
|
|
328
1193
|
$rootScope.obj = { name: {} };
|
|
1194
|
+
$rootScope.$digest();
|
|
329
1195
|
expect(logs).toEqual([
|
|
330
1196
|
{ newVal: { name: {} }, oldVal: { name: {} }, identical: true },
|
|
331
1197
|
]);
|
|
332
1198
|
logs = [];
|
|
333
1199
|
|
|
334
1200
|
$rootScope.obj.name.bar = "foo";
|
|
1201
|
+
$rootScope.$digest();
|
|
335
1202
|
expect(logs).toEqual([]);
|
|
336
1203
|
});
|
|
337
1204
|
|
|
338
1205
|
it("should watch object properties", () => {
|
|
339
1206
|
$rootScope.obj = {};
|
|
1207
|
+
$rootScope.$digest();
|
|
340
1208
|
expect(logs).toEqual([{ newVal: {}, oldVal: {}, identical: true }]);
|
|
341
1209
|
logs = [];
|
|
342
1210
|
$rootScope.obj.a = "A";
|
|
1211
|
+
$rootScope.$digest();
|
|
343
1212
|
expect(logs).toEqual([{ newVal: { a: "A" }, oldVal: {} }]);
|
|
344
1213
|
|
|
345
1214
|
logs = [];
|
|
346
1215
|
$rootScope.obj.a = "B";
|
|
1216
|
+
$rootScope.$digest();
|
|
347
1217
|
expect(logs).toEqual([{ newVal: { a: "B" }, oldVal: { a: "A" } }]);
|
|
348
1218
|
|
|
349
1219
|
logs = [];
|
|
350
1220
|
$rootScope.obj.b = [];
|
|
351
1221
|
$rootScope.obj.c = {};
|
|
1222
|
+
$rootScope.$digest();
|
|
352
1223
|
expect(logs).toEqual([
|
|
353
1224
|
{ newVal: { a: "B", b: [], c: {} }, oldVal: { a: "B" } },
|
|
354
1225
|
]);
|
|
@@ -357,6 +1228,7 @@ describe("Scope", function () {
|
|
|
357
1228
|
const temp = $rootScope.obj.a;
|
|
358
1229
|
$rootScope.obj.a = $rootScope.obj.b;
|
|
359
1230
|
$rootScope.obj.c = temp;
|
|
1231
|
+
$rootScope.$digest();
|
|
360
1232
|
expect(logs).toEqual([
|
|
361
1233
|
{
|
|
362
1234
|
newVal: { a: [], b: [], c: "B" },
|
|
@@ -366,6 +1238,7 @@ describe("Scope", function () {
|
|
|
366
1238
|
|
|
367
1239
|
logs = [];
|
|
368
1240
|
delete $rootScope.obj.a;
|
|
1241
|
+
$rootScope.$digest();
|
|
369
1242
|
expect(logs).toEqual([
|
|
370
1243
|
{ newVal: { b: [], c: "B" }, oldVal: { a: [], b: [], c: "B" } },
|
|
371
1244
|
]);
|
|
@@ -373,18 +1246,22 @@ describe("Scope", function () {
|
|
|
373
1246
|
|
|
374
1247
|
it("should not infinitely digest when current value is NaN", () => {
|
|
375
1248
|
$rootScope.obj = { a: NaN };
|
|
376
|
-
expect(() => {
|
|
1249
|
+
expect(() => {
|
|
1250
|
+
$rootScope.$digest();
|
|
1251
|
+
}).not.toThrow();
|
|
377
1252
|
});
|
|
378
1253
|
|
|
379
1254
|
it("should handle objects created using `Object.create(null)`", () => {
|
|
380
1255
|
$rootScope.obj = Object.create(null);
|
|
381
1256
|
$rootScope.obj.a = "a";
|
|
382
1257
|
$rootScope.obj.b = "b";
|
|
1258
|
+
$rootScope.$digest();
|
|
383
1259
|
expect(logs[0].newVal).toEqual(
|
|
384
1260
|
extend(Object.create(null), { a: "a", b: "b" }),
|
|
385
1261
|
);
|
|
386
1262
|
|
|
387
1263
|
delete $rootScope.obj.b;
|
|
1264
|
+
$rootScope.$digest();
|
|
388
1265
|
expect(logs[0].newVal).toEqual(
|
|
389
1266
|
extend(Object.create(null), { a: "a" }),
|
|
390
1267
|
);
|
|
@@ -410,6 +1287,7 @@ describe("Scope", function () {
|
|
|
410
1287
|
it("should return oldCollection === newCollection only on the first listener call", () => {
|
|
411
1288
|
// first time should be identical
|
|
412
1289
|
$rootScope.obj = "a";
|
|
1290
|
+
$rootScope.$digest();
|
|
413
1291
|
expect(logs).toEqual([
|
|
414
1292
|
{ newVal: ["a"], oldVal: ["a"], identical: true },
|
|
415
1293
|
]);
|
|
@@ -417,46 +1295,56 @@ describe("Scope", function () {
|
|
|
417
1295
|
|
|
418
1296
|
// second time should be different
|
|
419
1297
|
$rootScope.obj = "b";
|
|
1298
|
+
$rootScope.$digest();
|
|
420
1299
|
expect(logs).toEqual([{ newVal: ["b"], oldVal: ["a"] }]);
|
|
421
1300
|
});
|
|
422
1301
|
|
|
423
1302
|
it("should trigger when property changes into array", () => {
|
|
424
1303
|
$rootScope.obj = "test";
|
|
1304
|
+
$rootScope.$digest();
|
|
425
1305
|
expect(logs).toEqual([
|
|
426
1306
|
{ newVal: ["test"], oldVal: ["test"], identical: true },
|
|
427
1307
|
]);
|
|
428
1308
|
|
|
429
1309
|
logs = [];
|
|
430
1310
|
$rootScope.obj = [];
|
|
1311
|
+
$rootScope.$digest();
|
|
431
1312
|
expect(logs).toEqual([{ newVal: [[]], oldVal: ["test"] }]);
|
|
432
1313
|
|
|
433
1314
|
logs = [];
|
|
434
1315
|
$rootScope.obj = {};
|
|
1316
|
+
$rootScope.$digest();
|
|
435
1317
|
expect(logs).toEqual([{ newVal: [{}], oldVal: [[]] }]);
|
|
436
1318
|
|
|
437
1319
|
logs = [];
|
|
438
1320
|
$rootScope.obj = [];
|
|
1321
|
+
$rootScope.$digest();
|
|
439
1322
|
expect(logs).toEqual([{ newVal: [[]], oldVal: [{}] }]);
|
|
440
1323
|
|
|
441
1324
|
logs = [];
|
|
442
1325
|
$rootScope.obj = undefined;
|
|
1326
|
+
$rootScope.$digest();
|
|
443
1327
|
expect(logs).toEqual([{ newVal: [undefined], oldVal: [[]] }]);
|
|
444
1328
|
});
|
|
445
1329
|
|
|
446
1330
|
it("should not trigger change when object in collection changes", () => {
|
|
447
1331
|
$rootScope.obj = {};
|
|
1332
|
+
$rootScope.$digest();
|
|
448
1333
|
expect(logs).toEqual([
|
|
449
1334
|
{ newVal: [{}], oldVal: [{}], identical: true },
|
|
450
1335
|
]);
|
|
451
1336
|
|
|
452
1337
|
logs = [];
|
|
453
1338
|
$rootScope.obj.name = "foo";
|
|
1339
|
+
$rootScope.$digest();
|
|
454
1340
|
expect(logs).toEqual([]);
|
|
455
1341
|
});
|
|
456
1342
|
|
|
457
1343
|
it("should not infinitely digest when current value is NaN", () => {
|
|
458
1344
|
$rootScope.obj = NaN;
|
|
459
|
-
expect(() => {
|
|
1345
|
+
expect(() => {
|
|
1346
|
+
$rootScope.$digest();
|
|
1347
|
+
}).not.toThrow();
|
|
460
1348
|
});
|
|
461
1349
|
});
|
|
462
1350
|
|
|
@@ -477,6 +1365,7 @@ describe("Scope", function () {
|
|
|
477
1365
|
it("should return oldCollection === newCollection only on the first listener call", () => {
|
|
478
1366
|
$rootScope.obj = "b";
|
|
479
1367
|
// first time should be identical
|
|
1368
|
+
$rootScope.$digest();
|
|
480
1369
|
expect(logs).toEqual([
|
|
481
1370
|
{ newVal: { a: "b" }, oldVal: { a: "b" }, identical: true },
|
|
482
1371
|
]);
|
|
@@ -484,17 +1373,20 @@ describe("Scope", function () {
|
|
|
484
1373
|
// second time not identical
|
|
485
1374
|
logs = [];
|
|
486
1375
|
$rootScope.obj = "c";
|
|
1376
|
+
$rootScope.$digest();
|
|
487
1377
|
expect(logs).toEqual([{ newVal: { a: "c" }, oldVal: { a: "b" } }]);
|
|
488
1378
|
});
|
|
489
1379
|
|
|
490
1380
|
it("should trigger when property changes into object", () => {
|
|
491
1381
|
$rootScope.obj = "test";
|
|
1382
|
+
$rootScope.$digest();
|
|
492
1383
|
expect(logs).toEqual([
|
|
493
1384
|
{ newVal: { a: "test" }, oldVal: { a: "test" }, identical: true },
|
|
494
1385
|
]);
|
|
495
1386
|
|
|
496
1387
|
logs = [];
|
|
497
1388
|
$rootScope.obj = {};
|
|
1389
|
+
$rootScope.$digest();
|
|
498
1390
|
expect(logs).toEqual([
|
|
499
1391
|
{ newVal: { a: {} }, oldVal: { a: "test" } },
|
|
500
1392
|
]);
|
|
@@ -502,6 +1394,7 @@ describe("Scope", function () {
|
|
|
502
1394
|
|
|
503
1395
|
it("should not trigger change when object in collection changes", () => {
|
|
504
1396
|
$rootScope.obj = { name: "foo" };
|
|
1397
|
+
$rootScope.$digest();
|
|
505
1398
|
expect(logs).toEqual([
|
|
506
1399
|
{
|
|
507
1400
|
newVal: { a: { name: "foo" } },
|
|
@@ -512,29 +1405,35 @@ describe("Scope", function () {
|
|
|
512
1405
|
|
|
513
1406
|
logs = [];
|
|
514
1407
|
$rootScope.obj.name = "bar";
|
|
1408
|
+
$rootScope.$digest();
|
|
515
1409
|
expect(logs).toEqual([]);
|
|
516
1410
|
});
|
|
517
1411
|
|
|
518
1412
|
it("should watch object properties", () => {
|
|
519
1413
|
$rootScope.obj = {};
|
|
1414
|
+
$rootScope.$digest();
|
|
520
1415
|
expect(logs).toEqual([
|
|
521
1416
|
{ newVal: { a: {} }, oldVal: { a: {} }, identical: true },
|
|
522
1417
|
]);
|
|
523
1418
|
|
|
524
1419
|
logs = [];
|
|
525
1420
|
$rootScope.obj = "A";
|
|
1421
|
+
$rootScope.$digest();
|
|
526
1422
|
expect(logs).toEqual([{ newVal: { a: "A" }, oldVal: { a: {} } }]);
|
|
527
1423
|
|
|
528
1424
|
logs = [];
|
|
529
1425
|
$rootScope.obj = "B";
|
|
1426
|
+
$rootScope.$digest();
|
|
530
1427
|
expect(logs).toEqual([{ newVal: { a: "B" }, oldVal: { a: "A" } }]);
|
|
531
1428
|
|
|
532
1429
|
logs = [];
|
|
533
1430
|
$rootScope.obj = [];
|
|
1431
|
+
$rootScope.$digest();
|
|
534
1432
|
expect(logs).toEqual([{ newVal: { a: [] }, oldVal: { a: "B" } }]);
|
|
535
1433
|
|
|
536
1434
|
logs = [];
|
|
537
1435
|
delete $rootScope.obj;
|
|
1436
|
+
$rootScope.$digest();
|
|
538
1437
|
expect(logs).toEqual([
|
|
539
1438
|
{ newVal: { a: undefined }, oldVal: { a: [] } },
|
|
540
1439
|
]);
|
|
@@ -542,7 +1441,9 @@ describe("Scope", function () {
|
|
|
542
1441
|
|
|
543
1442
|
it("should not infinitely digest when current value is NaN", () => {
|
|
544
1443
|
$rootScope.obj = NaN;
|
|
545
|
-
expect(() => {
|
|
1444
|
+
expect(() => {
|
|
1445
|
+
$rootScope.$digest();
|
|
1446
|
+
}).not.toThrow();
|
|
546
1447
|
});
|
|
547
1448
|
});
|
|
548
1449
|
|
|
@@ -562,6 +1463,7 @@ describe("Scope", function () {
|
|
|
562
1463
|
|
|
563
1464
|
it('should default to "undefined" key', () => {
|
|
564
1465
|
$rootScope.obj = "test";
|
|
1466
|
+
$rootScope.$digest();
|
|
565
1467
|
expect(logs).toEqual([
|
|
566
1468
|
{
|
|
567
1469
|
newVal: { undefined: "test" },
|
|
@@ -574,18 +1476,21 @@ describe("Scope", function () {
|
|
|
574
1476
|
it("should trigger when key changes", () => {
|
|
575
1477
|
$rootScope.key = "a";
|
|
576
1478
|
$rootScope.obj = "test";
|
|
1479
|
+
$rootScope.$digest();
|
|
577
1480
|
expect(logs).toEqual([
|
|
578
1481
|
{ newVal: { a: "test" }, oldVal: { a: "test" }, identical: true },
|
|
579
1482
|
]);
|
|
580
1483
|
|
|
581
1484
|
logs = [];
|
|
582
1485
|
$rootScope.key = "b";
|
|
1486
|
+
$rootScope.$digest();
|
|
583
1487
|
expect(logs).toEqual([
|
|
584
1488
|
{ newVal: { b: "test" }, oldVal: { a: "test" } },
|
|
585
1489
|
]);
|
|
586
1490
|
|
|
587
1491
|
logs = [];
|
|
588
1492
|
$rootScope.key = true;
|
|
1493
|
+
$rootScope.$digest();
|
|
589
1494
|
expect(logs).toEqual([
|
|
590
1495
|
{ newVal: { true: "test" }, oldVal: { b: "test" } },
|
|
591
1496
|
]);
|
|
@@ -594,25 +1499,30 @@ describe("Scope", function () {
|
|
|
594
1499
|
it("should not trigger when key changes but stringified key does not", () => {
|
|
595
1500
|
$rootScope.key = 1;
|
|
596
1501
|
$rootScope.obj = "test";
|
|
1502
|
+
$rootScope.$digest();
|
|
597
1503
|
expect(logs).toEqual([
|
|
598
1504
|
{ newVal: { 1: "test" }, oldVal: { 1: "test" }, identical: true },
|
|
599
1505
|
]);
|
|
600
1506
|
|
|
601
1507
|
logs = [];
|
|
602
1508
|
$rootScope.key = "1";
|
|
1509
|
+
$rootScope.$digest();
|
|
603
1510
|
expect(logs).toEqual([]);
|
|
604
1511
|
|
|
605
1512
|
$rootScope.key = true;
|
|
1513
|
+
$rootScope.$digest();
|
|
606
1514
|
expect(logs).toEqual([
|
|
607
1515
|
{ newVal: { true: "test" }, oldVal: { 1: "test" } },
|
|
608
1516
|
]);
|
|
609
1517
|
|
|
610
1518
|
logs = [];
|
|
611
1519
|
$rootScope.key = "true";
|
|
1520
|
+
$rootScope.$digest();
|
|
612
1521
|
expect(logs).toEqual([]);
|
|
613
1522
|
|
|
614
1523
|
logs = [];
|
|
615
1524
|
$rootScope.key = {};
|
|
1525
|
+
$rootScope.$digest();
|
|
616
1526
|
expect(logs).toEqual([
|
|
617
1527
|
{
|
|
618
1528
|
newVal: { "[object Object]": "test" },
|
|
@@ -622,12 +1532,14 @@ describe("Scope", function () {
|
|
|
622
1532
|
|
|
623
1533
|
logs = [];
|
|
624
1534
|
$rootScope.key = {};
|
|
1535
|
+
$rootScope.$digest();
|
|
625
1536
|
expect(logs).toEqual([]);
|
|
626
1537
|
});
|
|
627
1538
|
|
|
628
1539
|
it("should not trigger change when object in collection changes", () => {
|
|
629
1540
|
$rootScope.key = "a";
|
|
630
1541
|
$rootScope.obj = { name: "foo" };
|
|
1542
|
+
$rootScope.$digest();
|
|
631
1543
|
expect(logs).toEqual([
|
|
632
1544
|
{
|
|
633
1545
|
newVal: { a: { name: "foo" } },
|
|
@@ -638,13 +1550,16 @@ describe("Scope", function () {
|
|
|
638
1550
|
logs = [];
|
|
639
1551
|
|
|
640
1552
|
$rootScope.obj.name = "bar";
|
|
1553
|
+
$rootScope.$digest();
|
|
641
1554
|
expect(logs).toEqual([]);
|
|
642
1555
|
});
|
|
643
1556
|
|
|
644
1557
|
it("should not infinitely digest when key value is NaN", () => {
|
|
645
1558
|
$rootScope.key = NaN;
|
|
646
1559
|
$rootScope.obj = NaN;
|
|
647
|
-
expect(() => {
|
|
1560
|
+
expect(() => {
|
|
1561
|
+
$rootScope.$digest();
|
|
1562
|
+
}).not.toThrow();
|
|
648
1563
|
});
|
|
649
1564
|
});
|
|
650
1565
|
});
|
|
@@ -655,6 +1570,7 @@ describe("Scope", function () {
|
|
|
655
1570
|
const watchSpy = jasmine.createSpy("watchSpy");
|
|
656
1571
|
$rootScope.$watch(watchSpy);
|
|
657
1572
|
$rootScope.$suspend();
|
|
1573
|
+
$rootScope.$digest();
|
|
658
1574
|
expect(watchSpy).not.toHaveBeenCalled();
|
|
659
1575
|
});
|
|
660
1576
|
|
|
@@ -663,6 +1579,7 @@ describe("Scope", function () {
|
|
|
663
1579
|
$rootScope.$watch(watchSpy);
|
|
664
1580
|
$rootScope.$suspend();
|
|
665
1581
|
$rootScope.$resume();
|
|
1582
|
+
$rootScope.$digest();
|
|
666
1583
|
expect(watchSpy).toHaveBeenCalled();
|
|
667
1584
|
});
|
|
668
1585
|
|
|
@@ -671,6 +1588,7 @@ describe("Scope", function () {
|
|
|
671
1588
|
const scope = $rootScope.$new(true);
|
|
672
1589
|
scope.$watch(watchSpy);
|
|
673
1590
|
$rootScope.$suspend();
|
|
1591
|
+
$rootScope.$digest();
|
|
674
1592
|
expect(watchSpy).not.toHaveBeenCalled();
|
|
675
1593
|
});
|
|
676
1594
|
|
|
@@ -680,6 +1598,7 @@ describe("Scope", function () {
|
|
|
680
1598
|
scope.$watch(watchSpy);
|
|
681
1599
|
$rootScope.$suspend();
|
|
682
1600
|
$rootScope.$resume();
|
|
1601
|
+
$rootScope.$digest();
|
|
683
1602
|
expect(watchSpy).toHaveBeenCalled();
|
|
684
1603
|
});
|
|
685
1604
|
|
|
@@ -746,6 +1665,7 @@ describe("Scope", function () {
|
|
|
746
1665
|
sibling.$watch(watchSpySibling);
|
|
747
1666
|
|
|
748
1667
|
child.$suspend();
|
|
1668
|
+
$rootScope.$digest();
|
|
749
1669
|
expect(watchSpyParent).toHaveBeenCalled();
|
|
750
1670
|
expect(watchSpyChild).not.toHaveBeenCalled();
|
|
751
1671
|
expect(watchSpySibling).toHaveBeenCalled();
|
|
@@ -786,8 +1706,123 @@ describe("Scope", function () {
|
|
|
786
1706
|
return scope.w3;
|
|
787
1707
|
}, log("w3action"));
|
|
788
1708
|
console.error(logs.length);
|
|
1709
|
+
scope.$digest();
|
|
789
1710
|
logs = [];
|
|
790
1711
|
}
|
|
1712
|
+
|
|
1713
|
+
describe("optimizations", () => {
|
|
1714
|
+
beforeEach(() => (logs = []));
|
|
1715
|
+
it("should check watches only once during an empty digest", () => {
|
|
1716
|
+
setupWatches($rootScope, console.log);
|
|
1717
|
+
$rootScope.$digest();
|
|
1718
|
+
expect(logs).toEqual(["w1", "w2", "w3"]);
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
it("should quit digest early after we check the last watch that was previously dirty", () => {
|
|
1722
|
+
setupWatches($rootScope, console.log);
|
|
1723
|
+
$rootScope.w1 = "x";
|
|
1724
|
+
$rootScope.$digest();
|
|
1725
|
+
expect(logs).toEqual(["w1", "w2", "w3", "w1"]);
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
it("should not quit digest early if a new watch was added from an existing watch action", () => {
|
|
1729
|
+
setupWatches($rootScope, console.log);
|
|
1730
|
+
$rootScope.$watch(
|
|
1731
|
+
() => {
|
|
1732
|
+
logs.push("w4");
|
|
1733
|
+
return "w4";
|
|
1734
|
+
},
|
|
1735
|
+
() => {
|
|
1736
|
+
logs.push("w4action");
|
|
1737
|
+
$rootScope.$watch(
|
|
1738
|
+
() => {
|
|
1739
|
+
logs.push("w5");
|
|
1740
|
+
return "w5";
|
|
1741
|
+
},
|
|
1742
|
+
() => logs.push("w5action"),
|
|
1743
|
+
);
|
|
1744
|
+
},
|
|
1745
|
+
);
|
|
1746
|
+
$rootScope.$digest();
|
|
1747
|
+
expect(logs).toEqual([
|
|
1748
|
+
"w1",
|
|
1749
|
+
"w2",
|
|
1750
|
+
"w3",
|
|
1751
|
+
"w4",
|
|
1752
|
+
"w4action",
|
|
1753
|
+
"w5",
|
|
1754
|
+
"w5action",
|
|
1755
|
+
"w1",
|
|
1756
|
+
"w2",
|
|
1757
|
+
"w3",
|
|
1758
|
+
"w4",
|
|
1759
|
+
"w5",
|
|
1760
|
+
]);
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
it("should not quit digest early if an evalAsync task was scheduled from a watch action", () => {
|
|
1764
|
+
setupWatches($rootScope, console.log);
|
|
1765
|
+
$rootScope.$watch(
|
|
1766
|
+
() => {
|
|
1767
|
+
logs.push("w4");
|
|
1768
|
+
return "w4";
|
|
1769
|
+
},
|
|
1770
|
+
() => {
|
|
1771
|
+
logs.push("w4action");
|
|
1772
|
+
$rootScope.$evalAsync(() => {
|
|
1773
|
+
logs.push("evalAsync");
|
|
1774
|
+
});
|
|
1775
|
+
},
|
|
1776
|
+
);
|
|
1777
|
+
$rootScope.$digest();
|
|
1778
|
+
expect(logs).toEqual([
|
|
1779
|
+
"w1",
|
|
1780
|
+
"w2",
|
|
1781
|
+
"w3",
|
|
1782
|
+
"w4",
|
|
1783
|
+
"w4action",
|
|
1784
|
+
"evalAsync",
|
|
1785
|
+
"w1",
|
|
1786
|
+
"w2",
|
|
1787
|
+
"w3",
|
|
1788
|
+
"w4",
|
|
1789
|
+
]);
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
it("should quit digest early but not too early when constious watches fire", () => {
|
|
1793
|
+
setupWatches($rootScope, console.log);
|
|
1794
|
+
$rootScope.$watch(
|
|
1795
|
+
() => {
|
|
1796
|
+
logs.push("w4");
|
|
1797
|
+
return $rootScope.w4;
|
|
1798
|
+
},
|
|
1799
|
+
(newVal) => {
|
|
1800
|
+
logs.push("w4action");
|
|
1801
|
+
$rootScope.w2 = newVal;
|
|
1802
|
+
},
|
|
1803
|
+
);
|
|
1804
|
+
|
|
1805
|
+
$rootScope.$digest();
|
|
1806
|
+
logs = [];
|
|
1807
|
+
|
|
1808
|
+
$rootScope.w1 = "x";
|
|
1809
|
+
$rootScope.w4 = "x";
|
|
1810
|
+
$rootScope.$digest();
|
|
1811
|
+
expect(logs).toEqual([
|
|
1812
|
+
"w1",
|
|
1813
|
+
"w2",
|
|
1814
|
+
"w3",
|
|
1815
|
+
"w4",
|
|
1816
|
+
"w4action",
|
|
1817
|
+
"w1",
|
|
1818
|
+
"w2",
|
|
1819
|
+
"w3",
|
|
1820
|
+
"w4",
|
|
1821
|
+
"w1",
|
|
1822
|
+
"w2",
|
|
1823
|
+
]);
|
|
1824
|
+
});
|
|
1825
|
+
});
|
|
791
1826
|
});
|
|
792
1827
|
|
|
793
1828
|
describe("$watchGroup", () => {
|
|
@@ -848,17 +1883,21 @@ describe("Scope", function () {
|
|
|
848
1883
|
|
|
849
1884
|
scope.a = "foo";
|
|
850
1885
|
scope.b = "bar";
|
|
1886
|
+
scope.$digest();
|
|
851
1887
|
expect(logs[0]).toEqual("foo,bar >>> foo,bar");
|
|
852
1888
|
|
|
853
1889
|
logs = [];
|
|
1890
|
+
scope.$digest();
|
|
854
1891
|
expect(logs).toEqual([]);
|
|
855
1892
|
|
|
856
1893
|
scope.a = "a";
|
|
1894
|
+
scope.$digest();
|
|
857
1895
|
expect(logs[0]).toEqual("foo,bar >>> a,bar");
|
|
858
1896
|
|
|
859
1897
|
logs = [];
|
|
860
1898
|
scope.a = "A";
|
|
861
1899
|
scope.b = "B";
|
|
1900
|
+
scope.$digest();
|
|
862
1901
|
expect(logs[0]).toEqual("a,bar >>> A,B");
|
|
863
1902
|
});
|
|
864
1903
|
|
|
@@ -870,12 +1909,15 @@ describe("Scope", function () {
|
|
|
870
1909
|
});
|
|
871
1910
|
|
|
872
1911
|
scope.a = "foo";
|
|
1912
|
+
scope.$digest();
|
|
873
1913
|
expect(logs[0]).toEqual("foo >>> foo");
|
|
874
1914
|
|
|
875
1915
|
logs = [];
|
|
1916
|
+
scope.$digest();
|
|
876
1917
|
expect(logs).toEqual([]);
|
|
877
1918
|
|
|
878
1919
|
scope.a = "bar";
|
|
1920
|
+
scope.$digest();
|
|
879
1921
|
expect(logs[0]).toEqual("foo >>> bar");
|
|
880
1922
|
});
|
|
881
1923
|
|
|
@@ -886,9 +1928,11 @@ describe("Scope", function () {
|
|
|
886
1928
|
});
|
|
887
1929
|
|
|
888
1930
|
expect(logs).toEqual([]);
|
|
1931
|
+
scope.$digest();
|
|
889
1932
|
expect(logs[0]).toEqual(" >>> ");
|
|
890
1933
|
|
|
891
1934
|
logs = [];
|
|
1935
|
+
scope.$digest();
|
|
892
1936
|
expect(logs).toEqual([]);
|
|
893
1937
|
});
|
|
894
1938
|
|
|
@@ -912,6 +1956,7 @@ describe("Scope", function () {
|
|
|
912
1956
|
deregisterNone();
|
|
913
1957
|
scope.a = "xxx";
|
|
914
1958
|
scope.b = "yyy";
|
|
1959
|
+
scope.$digest();
|
|
915
1960
|
expect(logs).toEqual([]);
|
|
916
1961
|
});
|
|
917
1962
|
|
|
@@ -1093,6 +2138,7 @@ describe("Scope", function () {
|
|
|
1093
2138
|
log += "3";
|
|
1094
2139
|
});
|
|
1095
2140
|
|
|
2141
|
+
$rootScope.$digest();
|
|
1096
2142
|
log = "";
|
|
1097
2143
|
});
|
|
1098
2144
|
|
|
@@ -1116,6 +2162,7 @@ describe("Scope", function () {
|
|
|
1116
2162
|
const spy = jasmine.createSpy("$watch spy");
|
|
1117
2163
|
$rootScope.$watch(spy);
|
|
1118
2164
|
$rootScope.$destroy();
|
|
2165
|
+
$rootScope.$digest();
|
|
1119
2166
|
expect(spy).not.toHaveBeenCalled();
|
|
1120
2167
|
});
|
|
1121
2168
|
|
|
@@ -1127,16 +2174,19 @@ describe("Scope", function () {
|
|
|
1127
2174
|
|
|
1128
2175
|
it("should remove first", () => {
|
|
1129
2176
|
first.$destroy();
|
|
2177
|
+
$rootScope.$digest();
|
|
1130
2178
|
expect(log).toEqual("23");
|
|
1131
2179
|
});
|
|
1132
2180
|
|
|
1133
2181
|
it("should remove middle", () => {
|
|
1134
2182
|
middle.$destroy();
|
|
2183
|
+
$rootScope.$digest();
|
|
1135
2184
|
expect(log).toEqual("13");
|
|
1136
2185
|
});
|
|
1137
2186
|
|
|
1138
2187
|
it("should remove last", () => {
|
|
1139
2188
|
last.$destroy();
|
|
2189
|
+
$rootScope.$digest();
|
|
1140
2190
|
expect(log).toEqual("12");
|
|
1141
2191
|
});
|
|
1142
2192
|
|
|
@@ -1150,6 +2200,7 @@ describe("Scope", function () {
|
|
|
1150
2200
|
});
|
|
1151
2201
|
|
|
1152
2202
|
it("should $destroy a scope only once and ignore any further destroy calls", () => {
|
|
2203
|
+
$rootScope.$digest();
|
|
1153
2204
|
expect(log).toBe("123");
|
|
1154
2205
|
|
|
1155
2206
|
first.$destroy();
|
|
@@ -1173,6 +2224,27 @@ describe("Scope", function () {
|
|
|
1173
2224
|
expect(logs).toEqual(["event"]);
|
|
1174
2225
|
});
|
|
1175
2226
|
|
|
2227
|
+
it("should decrement ancestor $$listenerCount entries", () => {
|
|
2228
|
+
const EVENT = "fooEvent";
|
|
2229
|
+
const spy = jasmine.createSpy("listener");
|
|
2230
|
+
const firstSecond = first.$new();
|
|
2231
|
+
|
|
2232
|
+
firstSecond.$on(EVENT, spy);
|
|
2233
|
+
firstSecond.$on(EVENT, spy);
|
|
2234
|
+
middle.$on(EVENT, spy);
|
|
2235
|
+
|
|
2236
|
+
expect($rootScope.$$listenerCount[EVENT]).toBe(3);
|
|
2237
|
+
expect(first.$$listenerCount[EVENT]).toBe(2);
|
|
2238
|
+
|
|
2239
|
+
firstSecond.$destroy();
|
|
2240
|
+
|
|
2241
|
+
expect($rootScope.$$listenerCount[EVENT]).toBe(1);
|
|
2242
|
+
expect(first.$$listenerCount[EVENT]).toBeUndefined();
|
|
2243
|
+
|
|
2244
|
+
$rootScope.$broadcast(EVENT);
|
|
2245
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
2246
|
+
});
|
|
2247
|
+
|
|
1176
2248
|
it("should do nothing when a child event listener is registered after parent's destruction", () => {
|
|
1177
2249
|
const parent = $rootScope.$new();
|
|
1178
2250
|
const child = parent.$new();
|
|
@@ -1215,6 +2287,20 @@ describe("Scope", function () {
|
|
|
1215
2287
|
expect(called).toBe(false);
|
|
1216
2288
|
});
|
|
1217
2289
|
|
|
2290
|
+
it("should do nothing when $evalAsync()ing after parent's destruction", () => {
|
|
2291
|
+
const parent = $rootScope.$new();
|
|
2292
|
+
const child = parent.$new();
|
|
2293
|
+
|
|
2294
|
+
parent.$destroy();
|
|
2295
|
+
|
|
2296
|
+
let called = false;
|
|
2297
|
+
function applyFunc() {
|
|
2298
|
+
called = true;
|
|
2299
|
+
}
|
|
2300
|
+
child.$evalAsync(applyFunc);
|
|
2301
|
+
expect(called).toBe(false);
|
|
2302
|
+
});
|
|
2303
|
+
|
|
1218
2304
|
it("should preserve all (own and inherited) model properties on a destroyed scope", () => {
|
|
1219
2305
|
// This test simulates an async task (xhr response) interacting with the scope after the scope
|
|
1220
2306
|
// was destroyed. Since we can't abort the request, we should ensure that the task doesn't
|
|
@@ -1233,6 +2319,238 @@ describe("Scope", function () {
|
|
|
1233
2319
|
});
|
|
1234
2320
|
});
|
|
1235
2321
|
|
|
2322
|
+
describe("$eval", () => {
|
|
2323
|
+
it("should eval an expression", () => {
|
|
2324
|
+
expect($rootScope.$eval("a=1")).toEqual(1);
|
|
2325
|
+
expect($rootScope.a).toEqual(1);
|
|
2326
|
+
|
|
2327
|
+
$rootScope.$eval((self) => {
|
|
2328
|
+
self.b = 2;
|
|
2329
|
+
});
|
|
2330
|
+
expect($rootScope.b).toEqual(2);
|
|
2331
|
+
});
|
|
2332
|
+
|
|
2333
|
+
it("should allow passing locals to the expression", () => {
|
|
2334
|
+
expect($rootScope.$eval("a+1", { a: 2 })).toBe(3);
|
|
2335
|
+
|
|
2336
|
+
$rootScope.$eval(
|
|
2337
|
+
(scope, locals) => {
|
|
2338
|
+
scope.c = locals.b + 4;
|
|
2339
|
+
},
|
|
2340
|
+
{ b: 3 },
|
|
2341
|
+
);
|
|
2342
|
+
expect($rootScope.c).toBe(7);
|
|
2343
|
+
});
|
|
2344
|
+
});
|
|
2345
|
+
|
|
2346
|
+
describe("$evalAsync", () => {
|
|
2347
|
+
it("should run callback before $watch", () => {
|
|
2348
|
+
let log = "";
|
|
2349
|
+
const child = $rootScope.$new();
|
|
2350
|
+
$rootScope.$evalAsync((scope) => {
|
|
2351
|
+
log += "parent.async;";
|
|
2352
|
+
});
|
|
2353
|
+
$rootScope.$watch("value", () => {
|
|
2354
|
+
log += "parent.$digest;";
|
|
2355
|
+
});
|
|
2356
|
+
child.$evalAsync((scope) => {
|
|
2357
|
+
log += "child.async;";
|
|
2358
|
+
});
|
|
2359
|
+
child.$watch("value", () => {
|
|
2360
|
+
log += "child.$digest;";
|
|
2361
|
+
});
|
|
2362
|
+
$rootScope.$digest();
|
|
2363
|
+
expect(log).toEqual(
|
|
2364
|
+
"parent.async;child.async;parent.$digest;child.$digest;",
|
|
2365
|
+
);
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
it("should not run another digest for an $$postDigest call", () => {
|
|
2369
|
+
let internalWatchCount = 0;
|
|
2370
|
+
let externalWatchCount = 0;
|
|
2371
|
+
|
|
2372
|
+
$rootScope.internalCount = 0;
|
|
2373
|
+
$rootScope.externalCount = 0;
|
|
2374
|
+
|
|
2375
|
+
$rootScope.$evalAsync((scope) => {
|
|
2376
|
+
$rootScope.internalCount++;
|
|
2377
|
+
});
|
|
2378
|
+
|
|
2379
|
+
$rootScope.$$postDigest((scope) => {
|
|
2380
|
+
$rootScope.externalCount++;
|
|
2381
|
+
});
|
|
2382
|
+
|
|
2383
|
+
$rootScope.$watch("internalCount", (value) => {
|
|
2384
|
+
internalWatchCount = value;
|
|
2385
|
+
});
|
|
2386
|
+
$rootScope.$watch("externalCount", (value) => {
|
|
2387
|
+
externalWatchCount = value;
|
|
2388
|
+
});
|
|
2389
|
+
|
|
2390
|
+
$rootScope.$digest();
|
|
2391
|
+
|
|
2392
|
+
expect(internalWatchCount).toEqual(1);
|
|
2393
|
+
expect(externalWatchCount).toEqual(0);
|
|
2394
|
+
});
|
|
2395
|
+
|
|
2396
|
+
it("should cause a $digest rerun", () => {
|
|
2397
|
+
$rootScope.log = "";
|
|
2398
|
+
$rootScope.value = 0;
|
|
2399
|
+
$rootScope.$watch("value", () => {
|
|
2400
|
+
$rootScope.log += ".";
|
|
2401
|
+
});
|
|
2402
|
+
$rootScope.$watch("init", () => {
|
|
2403
|
+
$rootScope.$evalAsync('value = 123; log = log + "=" ');
|
|
2404
|
+
expect($rootScope.value).toEqual(0);
|
|
2405
|
+
});
|
|
2406
|
+
$rootScope.$digest();
|
|
2407
|
+
expect($rootScope.log).toEqual(".=.");
|
|
2408
|
+
});
|
|
2409
|
+
|
|
2410
|
+
it("should run async in the same order as added", () => {
|
|
2411
|
+
$rootScope.log = "";
|
|
2412
|
+
$rootScope.$evalAsync("log = log + 1");
|
|
2413
|
+
$rootScope.$evalAsync("log = log + 2");
|
|
2414
|
+
$rootScope.$digest();
|
|
2415
|
+
expect($rootScope.log).toBe("12");
|
|
2416
|
+
});
|
|
2417
|
+
|
|
2418
|
+
it("should allow passing locals to the expression", () => {
|
|
2419
|
+
$rootScope.log = "";
|
|
2420
|
+
$rootScope.$evalAsync("log = log + a", { a: 1 });
|
|
2421
|
+
$rootScope.$digest();
|
|
2422
|
+
expect($rootScope.log).toBe("1");
|
|
2423
|
+
});
|
|
2424
|
+
|
|
2425
|
+
it("should run async expressions in their proper context", () => {
|
|
2426
|
+
const child = $rootScope.$new();
|
|
2427
|
+
$rootScope.ctx = "root context";
|
|
2428
|
+
$rootScope.log = "";
|
|
2429
|
+
child.ctx = "child context";
|
|
2430
|
+
child.log = "";
|
|
2431
|
+
child.$evalAsync("log=ctx");
|
|
2432
|
+
$rootScope.$digest();
|
|
2433
|
+
expect($rootScope.log).toBe("");
|
|
2434
|
+
expect(child.log).toBe("child context");
|
|
2435
|
+
});
|
|
2436
|
+
|
|
2437
|
+
it("should operate only with a single queue across all child and isolate scopes", () => {
|
|
2438
|
+
const childScope = $rootScope.$new();
|
|
2439
|
+
const isolateScope = $rootScope.$new(true);
|
|
2440
|
+
|
|
2441
|
+
$rootScope.$evalAsync("rootExpression");
|
|
2442
|
+
childScope.$evalAsync("childExpression");
|
|
2443
|
+
isolateScope.$evalAsync("isolateExpression");
|
|
2444
|
+
expect($$asyncQueue).toEqual([
|
|
2445
|
+
{
|
|
2446
|
+
scope: $rootScope,
|
|
2447
|
+
fn: $parse("rootExpression"),
|
|
2448
|
+
locals: undefined,
|
|
2449
|
+
},
|
|
2450
|
+
{
|
|
2451
|
+
scope: childScope,
|
|
2452
|
+
fn: $parse("childExpression"),
|
|
2453
|
+
locals: undefined,
|
|
2454
|
+
},
|
|
2455
|
+
{
|
|
2456
|
+
scope: isolateScope,
|
|
2457
|
+
fn: $parse("isolateExpression"),
|
|
2458
|
+
locals: undefined,
|
|
2459
|
+
},
|
|
2460
|
+
]);
|
|
2461
|
+
});
|
|
2462
|
+
|
|
2463
|
+
describe("auto-flushing when queueing outside of an $apply", () => {
|
|
2464
|
+
it("should auto-flush the queue asynchronously and trigger digest", () => {
|
|
2465
|
+
logs = [];
|
|
2466
|
+
$rootScope.$evalAsync(() => {
|
|
2467
|
+
logs.push("eval-ed!");
|
|
2468
|
+
return "eval-ed!";
|
|
2469
|
+
});
|
|
2470
|
+
$rootScope.$watch(() => {
|
|
2471
|
+
logs.push("digesting");
|
|
2472
|
+
return "digesting";
|
|
2473
|
+
});
|
|
2474
|
+
expect(logs).toEqual([]);
|
|
2475
|
+
setTimeout(() => {
|
|
2476
|
+
expect(logs).toEqual(["eval-ed!", "digesting", "digesting"]);
|
|
2477
|
+
});
|
|
2478
|
+
});
|
|
2479
|
+
|
|
2480
|
+
it("should not trigger digest asynchronously if the queue is empty in the next tick", () => {
|
|
2481
|
+
logs = [];
|
|
2482
|
+
$rootScope.$evalAsync(() => {
|
|
2483
|
+
logs.push("eval-ed!");
|
|
2484
|
+
return "eval-ed!";
|
|
2485
|
+
});
|
|
2486
|
+
$rootScope.$watch(() => {
|
|
2487
|
+
logs.push("digesting");
|
|
2488
|
+
return "digesting";
|
|
2489
|
+
});
|
|
2490
|
+
expect(logs).toEqual([]);
|
|
2491
|
+
|
|
2492
|
+
$rootScope.$digest();
|
|
2493
|
+
|
|
2494
|
+
expect(logs).toEqual(["eval-ed!", "digesting", "digesting"]);
|
|
2495
|
+
logs = [];
|
|
2496
|
+
|
|
2497
|
+
setTimeout(() => {
|
|
2498
|
+
expect(logs).toEqual([]);
|
|
2499
|
+
});
|
|
2500
|
+
});
|
|
2501
|
+
|
|
2502
|
+
it("should not schedule more than one auto-flush task", () => {
|
|
2503
|
+
logs = [];
|
|
2504
|
+
$rootScope.$evalAsync(() => {
|
|
2505
|
+
logs.push("eval-ed 1!");
|
|
2506
|
+
return "eval-ed 1!";
|
|
2507
|
+
});
|
|
2508
|
+
$rootScope.$evalAsync(() => {
|
|
2509
|
+
logs.push("eval-ed 2!");
|
|
2510
|
+
return "eval-ed 2!";
|
|
2511
|
+
});
|
|
2512
|
+
expect(logs).toEqual([]);
|
|
2513
|
+
setTimeout(() => {
|
|
2514
|
+
expect(logs).toEqual(["eval-ed 1!", "eval-ed 2!"]);
|
|
2515
|
+
});
|
|
2516
|
+
|
|
2517
|
+
setTimeout(() => {
|
|
2518
|
+
expect(logs).toEqual(["eval-ed 1!", "eval-ed 2!"]);
|
|
2519
|
+
});
|
|
2520
|
+
});
|
|
2521
|
+
|
|
2522
|
+
it("should not have execution affected by an explicit $digest call", () => {
|
|
2523
|
+
const scope1 = $rootScope.$new();
|
|
2524
|
+
const scope2 = $rootScope.$new();
|
|
2525
|
+
|
|
2526
|
+
scope1.$watch("value", (value) => {
|
|
2527
|
+
scope1.result = value;
|
|
2528
|
+
});
|
|
2529
|
+
|
|
2530
|
+
scope1.$evalAsync(() => {
|
|
2531
|
+
scope1.value = "bar";
|
|
2532
|
+
});
|
|
2533
|
+
|
|
2534
|
+
expect(scope1.result).toBe(undefined);
|
|
2535
|
+
scope2.$digest();
|
|
2536
|
+
|
|
2537
|
+
setTimeout(() => expect(scope1.result).toBe("bar"));
|
|
2538
|
+
});
|
|
2539
|
+
});
|
|
2540
|
+
|
|
2541
|
+
it("should not pass anything as `this` to scheduled functions", () => {
|
|
2542
|
+
let this1 = {};
|
|
2543
|
+
const this2 = (function () {
|
|
2544
|
+
return this;
|
|
2545
|
+
})();
|
|
2546
|
+
$rootScope.$evalAsync(function () {
|
|
2547
|
+
this1 = this;
|
|
2548
|
+
});
|
|
2549
|
+
$rootScope.$digest();
|
|
2550
|
+
expect(this1).toEqual(this2);
|
|
2551
|
+
});
|
|
2552
|
+
});
|
|
2553
|
+
|
|
1236
2554
|
describe("$apply", () => {
|
|
1237
2555
|
beforeEach(() => (logs = []));
|
|
1238
2556
|
|
|
@@ -1287,6 +2605,7 @@ describe("Scope", function () {
|
|
|
1287
2605
|
$rootScope.$watch(() => {
|
|
1288
2606
|
log += "$digest;";
|
|
1289
2607
|
});
|
|
2608
|
+
$rootScope.$digest();
|
|
1290
2609
|
log = "";
|
|
1291
2610
|
});
|
|
1292
2611
|
|
|
@@ -1436,98 +2755,714 @@ describe("Scope", function () {
|
|
|
1436
2755
|
const expression = jasmine.createSpy("expr");
|
|
1437
2756
|
|
|
1438
2757
|
$rootScope.$applyAsync(expression);
|
|
2758
|
+
$rootScope.$digest();
|
|
1439
2759
|
expect(expression).toHaveBeenCalled();
|
|
1440
2760
|
expect(cancel).toHaveBeenCalled();
|
|
1441
2761
|
expression.calls.reset();
|
|
1442
2762
|
cancel.calls.reset();
|
|
1443
2763
|
|
|
1444
2764
|
// assert that another digest won't call the function again
|
|
2765
|
+
$rootScope.$digest();
|
|
1445
2766
|
expect(expression).not.toHaveBeenCalled();
|
|
1446
2767
|
expect(cancel).not.toHaveBeenCalled();
|
|
1447
2768
|
});
|
|
1448
2769
|
});
|
|
1449
2770
|
|
|
1450
|
-
describe("
|
|
2771
|
+
describe("$$postDigest", () => {
|
|
1451
2772
|
beforeEach(() => (logs = []));
|
|
1452
2773
|
it("should process callbacks as a queue (FIFO) when the scope is digested", () => {
|
|
1453
2774
|
let signature = "";
|
|
1454
2775
|
|
|
1455
|
-
$rootScope
|
|
2776
|
+
$rootScope.$$postDigest(() => {
|
|
1456
2777
|
signature += "A";
|
|
1457
|
-
$rootScope
|
|
2778
|
+
$rootScope.$$postDigest(() => {
|
|
1458
2779
|
signature += "D";
|
|
1459
2780
|
});
|
|
1460
2781
|
});
|
|
1461
2782
|
|
|
1462
|
-
$rootScope
|
|
2783
|
+
$rootScope.$$postDigest(() => {
|
|
1463
2784
|
signature += "B";
|
|
1464
2785
|
});
|
|
1465
2786
|
|
|
1466
|
-
$rootScope
|
|
2787
|
+
$rootScope.$$postDigest(() => {
|
|
1467
2788
|
signature += "C";
|
|
1468
2789
|
});
|
|
1469
2790
|
|
|
1470
2791
|
expect(signature).toBe("");
|
|
2792
|
+
$rootScope.$digest();
|
|
1471
2793
|
expect(signature).toBe("ABCD");
|
|
1472
2794
|
});
|
|
1473
2795
|
|
|
1474
|
-
it("should support $apply calls nested in
|
|
2796
|
+
it("should support $apply calls nested in $$postDigest callbacks", () => {
|
|
1475
2797
|
let signature = "";
|
|
1476
2798
|
|
|
1477
|
-
$rootScope
|
|
2799
|
+
$rootScope.$$postDigest(() => {
|
|
1478
2800
|
signature += "A";
|
|
1479
2801
|
});
|
|
1480
2802
|
|
|
1481
|
-
$rootScope
|
|
2803
|
+
$rootScope.$$postDigest(() => {
|
|
1482
2804
|
signature += "B";
|
|
1483
2805
|
$rootScope.$apply();
|
|
1484
2806
|
signature += "D";
|
|
1485
2807
|
});
|
|
1486
2808
|
|
|
1487
|
-
$rootScope
|
|
2809
|
+
$rootScope.$$postDigest(() => {
|
|
1488
2810
|
signature += "C";
|
|
1489
2811
|
});
|
|
1490
2812
|
|
|
1491
2813
|
expect(signature).toBe("");
|
|
2814
|
+
$rootScope.$digest();
|
|
1492
2815
|
expect(signature).toBe("ABCD");
|
|
1493
2816
|
});
|
|
1494
2817
|
|
|
1495
|
-
it("should run a
|
|
2818
|
+
it("should run a $$postDigest call on all child scopes when a parent scope is digested", () => {
|
|
1496
2819
|
const parent = $rootScope.$new();
|
|
1497
2820
|
const child = parent.$new();
|
|
1498
2821
|
let count = 0;
|
|
1499
2822
|
|
|
1500
|
-
$rootScope
|
|
2823
|
+
$rootScope.$$postDigest(() => {
|
|
1501
2824
|
count++;
|
|
1502
2825
|
});
|
|
1503
2826
|
|
|
1504
|
-
parent
|
|
2827
|
+
parent.$$postDigest(() => {
|
|
1505
2828
|
count++;
|
|
1506
2829
|
});
|
|
1507
2830
|
|
|
1508
|
-
child
|
|
2831
|
+
child.$$postDigest(() => {
|
|
1509
2832
|
count++;
|
|
1510
2833
|
});
|
|
1511
2834
|
|
|
1512
2835
|
expect(count).toBe(0);
|
|
2836
|
+
$rootScope.$digest();
|
|
1513
2837
|
expect(count).toBe(3);
|
|
1514
2838
|
});
|
|
1515
2839
|
|
|
1516
|
-
it("should run a
|
|
2840
|
+
it("should run a $$postDigest call even if the child scope is isolated", () => {
|
|
1517
2841
|
const parent = $rootScope.$new();
|
|
1518
2842
|
const child = parent.$new(true);
|
|
1519
2843
|
let signature = "";
|
|
1520
2844
|
|
|
1521
|
-
parent
|
|
2845
|
+
parent.$$postDigest(() => {
|
|
1522
2846
|
signature += "A";
|
|
1523
2847
|
});
|
|
1524
2848
|
|
|
1525
|
-
child
|
|
2849
|
+
child.$$postDigest(() => {
|
|
1526
2850
|
signature += "B";
|
|
1527
2851
|
});
|
|
1528
2852
|
|
|
1529
2853
|
expect(signature).toBe("");
|
|
2854
|
+
$rootScope.$digest();
|
|
1530
2855
|
expect(signature).toBe("AB");
|
|
1531
2856
|
});
|
|
1532
2857
|
});
|
|
2858
|
+
|
|
2859
|
+
describe("events", () => {
|
|
2860
|
+
describe("$on", () => {
|
|
2861
|
+
it("should add listener for both $emit and $broadcast events", () => {
|
|
2862
|
+
logs = "";
|
|
2863
|
+
const child = $rootScope.$new();
|
|
2864
|
+
|
|
2865
|
+
function eventFn() {
|
|
2866
|
+
logs += "X";
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
child.$on("abc", eventFn);
|
|
2870
|
+
expect(logs).toEqual("");
|
|
2871
|
+
|
|
2872
|
+
child.$emit("abc");
|
|
2873
|
+
expect(logs).toEqual("X");
|
|
2874
|
+
|
|
2875
|
+
child.$broadcast("abc");
|
|
2876
|
+
expect(logs).toEqual("XX");
|
|
2877
|
+
});
|
|
2878
|
+
|
|
2879
|
+
it("should increment ancestor $$listenerCount entries", () => {
|
|
2880
|
+
const child1 = $rootScope.$new();
|
|
2881
|
+
const child2 = child1.$new();
|
|
2882
|
+
const spy = jasmine.createSpy();
|
|
2883
|
+
|
|
2884
|
+
$rootScope.$on("event1", spy);
|
|
2885
|
+
expect($rootScope.$$listenerCount.event1).toEqual(1);
|
|
2886
|
+
|
|
2887
|
+
child1.$on("event1", spy);
|
|
2888
|
+
expect($rootScope.$$listenerCount.event1).toEqual(2);
|
|
2889
|
+
expect(child1.$$listenerCount.event1).toEqual(1);
|
|
2890
|
+
|
|
2891
|
+
child2.$on("event2", spy);
|
|
2892
|
+
expect($rootScope.$$listenerCount.event1).toEqual(2);
|
|
2893
|
+
expect($rootScope.$$listenerCount.event2).toEqual(1);
|
|
2894
|
+
expect(child1.$$listenerCount.event1).toEqual(1);
|
|
2895
|
+
expect(child1.$$listenerCount.event2).toEqual(1);
|
|
2896
|
+
expect(child2.$$listenerCount.event2).toEqual(1);
|
|
2897
|
+
});
|
|
2898
|
+
|
|
2899
|
+
describe("deregistration", () => {
|
|
2900
|
+
it("should return a function that deregisters the listener", () => {
|
|
2901
|
+
let log = "";
|
|
2902
|
+
const child = $rootScope.$new();
|
|
2903
|
+
let listenerRemove;
|
|
2904
|
+
|
|
2905
|
+
function eventFn() {
|
|
2906
|
+
log += "X";
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
listenerRemove = child.$on("abc", eventFn);
|
|
2910
|
+
expect(log).toEqual("");
|
|
2911
|
+
expect(listenerRemove).toBeDefined();
|
|
2912
|
+
|
|
2913
|
+
child.$emit("abc");
|
|
2914
|
+
child.$broadcast("abc");
|
|
2915
|
+
expect(log).toEqual("XX");
|
|
2916
|
+
expect($rootScope.$$listenerCount.abc).toBe(1);
|
|
2917
|
+
|
|
2918
|
+
log = "";
|
|
2919
|
+
listenerRemove();
|
|
2920
|
+
child.$emit("abc");
|
|
2921
|
+
child.$broadcast("abc");
|
|
2922
|
+
expect(log).toEqual("");
|
|
2923
|
+
expect($rootScope.$$listenerCount.abc).toBeUndefined();
|
|
2924
|
+
});
|
|
2925
|
+
|
|
2926
|
+
// See issue https://github.com/angular/angular.js/issues/16135
|
|
2927
|
+
it("should deallocate the listener array entry", () => {
|
|
2928
|
+
const remove1 = $rootScope.$on("abc", () => {});
|
|
2929
|
+
$rootScope.$on("abc", () => {});
|
|
2930
|
+
|
|
2931
|
+
expect($rootScope.$$listeners.get("abc").length).toBe(2);
|
|
2932
|
+
expect(0 in $rootScope.$$listeners.get("abc")).toBe(true);
|
|
2933
|
+
|
|
2934
|
+
remove1();
|
|
2935
|
+
|
|
2936
|
+
expect($rootScope.$$listeners.get("abc").length).toBe(2);
|
|
2937
|
+
expect(0 in $rootScope.$$listeners.get("abc")).toBe(false);
|
|
2938
|
+
});
|
|
2939
|
+
|
|
2940
|
+
it("should call next listener after removing the current listener via its own handler", () => {
|
|
2941
|
+
const listener1 = jasmine.createSpy("listener1").and.callFake(() => {
|
|
2942
|
+
remove1();
|
|
2943
|
+
});
|
|
2944
|
+
let remove1 = $rootScope.$on("abc", listener1);
|
|
2945
|
+
|
|
2946
|
+
const listener2 = jasmine.createSpy("listener2");
|
|
2947
|
+
const remove2 = $rootScope.$on("abc", listener2);
|
|
2948
|
+
|
|
2949
|
+
const listener3 = jasmine.createSpy("listener3");
|
|
2950
|
+
const remove3 = $rootScope.$on("abc", listener3);
|
|
2951
|
+
|
|
2952
|
+
$rootScope.$broadcast("abc");
|
|
2953
|
+
expect(listener1).toHaveBeenCalled();
|
|
2954
|
+
expect(listener2).toHaveBeenCalled();
|
|
2955
|
+
expect(listener3).toHaveBeenCalled();
|
|
2956
|
+
|
|
2957
|
+
listener1.calls.reset();
|
|
2958
|
+
listener2.calls.reset();
|
|
2959
|
+
listener3.calls.reset();
|
|
2960
|
+
|
|
2961
|
+
$rootScope.$broadcast("abc");
|
|
2962
|
+
expect(listener1).not.toHaveBeenCalled();
|
|
2963
|
+
expect(listener2).toHaveBeenCalled();
|
|
2964
|
+
expect(listener3).toHaveBeenCalled();
|
|
2965
|
+
});
|
|
2966
|
+
|
|
2967
|
+
it("should call all subsequent listeners when a previous listener is removed via a handler", () => {
|
|
2968
|
+
const listener1 = jasmine.createSpy();
|
|
2969
|
+
const remove1 = $rootScope.$on("abc", listener1);
|
|
2970
|
+
|
|
2971
|
+
const listener2 = jasmine.createSpy().and.callFake(remove1);
|
|
2972
|
+
const remove2 = $rootScope.$on("abc", listener2);
|
|
2973
|
+
|
|
2974
|
+
const listener3 = jasmine.createSpy();
|
|
2975
|
+
const remove3 = $rootScope.$on("abc", listener3);
|
|
2976
|
+
|
|
2977
|
+
$rootScope.$broadcast("abc");
|
|
2978
|
+
expect(listener1).toHaveBeenCalled();
|
|
2979
|
+
expect(listener2).toHaveBeenCalled();
|
|
2980
|
+
expect(listener3).toHaveBeenCalled();
|
|
2981
|
+
|
|
2982
|
+
listener1.calls.reset();
|
|
2983
|
+
listener2.calls.reset();
|
|
2984
|
+
listener3.calls.reset();
|
|
2985
|
+
|
|
2986
|
+
$rootScope.$broadcast("abc");
|
|
2987
|
+
expect(listener1).not.toHaveBeenCalled();
|
|
2988
|
+
expect(listener2).toHaveBeenCalled();
|
|
2989
|
+
expect(listener3).toHaveBeenCalled();
|
|
2990
|
+
});
|
|
2991
|
+
|
|
2992
|
+
it("should not call listener when removed by previous", () => {
|
|
2993
|
+
const listener1 = jasmine.createSpy("listener1");
|
|
2994
|
+
const remove1 = $rootScope.$on("abc", listener1);
|
|
2995
|
+
|
|
2996
|
+
const listener2 = jasmine.createSpy("listener2").and.callFake(() => {
|
|
2997
|
+
remove3();
|
|
2998
|
+
});
|
|
2999
|
+
const remove2 = $rootScope.$on("abc", listener2);
|
|
3000
|
+
|
|
3001
|
+
const listener3 = jasmine.createSpy("listener3");
|
|
3002
|
+
let remove3 = $rootScope.$on("abc", listener3);
|
|
3003
|
+
|
|
3004
|
+
const listener4 = jasmine.createSpy("listener4");
|
|
3005
|
+
const remove4 = $rootScope.$on("abc", listener4);
|
|
3006
|
+
|
|
3007
|
+
$rootScope.$broadcast("abc");
|
|
3008
|
+
expect(listener1).toHaveBeenCalled();
|
|
3009
|
+
expect(listener2).toHaveBeenCalled();
|
|
3010
|
+
expect(listener3).not.toHaveBeenCalled();
|
|
3011
|
+
expect(listener4).toHaveBeenCalled();
|
|
3012
|
+
|
|
3013
|
+
listener1.calls.reset();
|
|
3014
|
+
listener2.calls.reset();
|
|
3015
|
+
listener3.calls.reset();
|
|
3016
|
+
listener4.calls.reset();
|
|
3017
|
+
|
|
3018
|
+
$rootScope.$broadcast("abc");
|
|
3019
|
+
expect(listener1).toHaveBeenCalled();
|
|
3020
|
+
expect(listener2).toHaveBeenCalled();
|
|
3021
|
+
expect(listener3).not.toHaveBeenCalled();
|
|
3022
|
+
expect(listener4).toHaveBeenCalled();
|
|
3023
|
+
});
|
|
3024
|
+
|
|
3025
|
+
it("should decrement ancestor $$listenerCount entries", () => {
|
|
3026
|
+
const child1 = $rootScope.$new();
|
|
3027
|
+
const child2 = child1.$new();
|
|
3028
|
+
const spy = jasmine.createSpy();
|
|
3029
|
+
|
|
3030
|
+
$rootScope.$on("event1", spy);
|
|
3031
|
+
expect($rootScope.$$listenerCount.event1).toEqual(1);
|
|
3032
|
+
|
|
3033
|
+
child1.$on("event1", spy);
|
|
3034
|
+
expect($rootScope.$$listenerCount.event1).toEqual(2);
|
|
3035
|
+
expect(child1.$$listenerCount.event1).toEqual(1);
|
|
3036
|
+
|
|
3037
|
+
const deregisterEvent2Listener = child2.$on("event2", spy);
|
|
3038
|
+
expect($rootScope.$$listenerCount.event1).toEqual(2);
|
|
3039
|
+
expect($rootScope.$$listenerCount.event2).toEqual(1);
|
|
3040
|
+
expect(child1.$$listenerCount.event1).toEqual(1);
|
|
3041
|
+
expect(child1.$$listenerCount.event2).toEqual(1);
|
|
3042
|
+
expect(child2.$$listenerCount.event2).toEqual(1);
|
|
3043
|
+
|
|
3044
|
+
deregisterEvent2Listener();
|
|
3045
|
+
|
|
3046
|
+
expect($rootScope.$$listenerCount.event1).toEqual(2);
|
|
3047
|
+
expect(child1.$$listenerCount.event1).toEqual(1);
|
|
3048
|
+
expect(child2.$$listenerCount).toBeTruthy();
|
|
3049
|
+
});
|
|
3050
|
+
|
|
3051
|
+
it("should not decrement $$listenerCount when called second time", () => {
|
|
3052
|
+
const child = $rootScope.$new();
|
|
3053
|
+
const listener1Spy = jasmine.createSpy();
|
|
3054
|
+
const listener2Spy = jasmine.createSpy();
|
|
3055
|
+
|
|
3056
|
+
child.$on("abc", listener1Spy);
|
|
3057
|
+
expect($rootScope.$$listenerCount.abc).toEqual(1);
|
|
3058
|
+
expect(child.$$listenerCount.abc).toEqual(1);
|
|
3059
|
+
|
|
3060
|
+
const deregisterEventListener = child.$on("abc", listener2Spy);
|
|
3061
|
+
expect($rootScope.$$listenerCount.abc).toEqual(2);
|
|
3062
|
+
expect(child.$$listenerCount.abc).toEqual(2);
|
|
3063
|
+
|
|
3064
|
+
deregisterEventListener();
|
|
3065
|
+
|
|
3066
|
+
expect($rootScope.$$listenerCount.abc).toEqual(1);
|
|
3067
|
+
expect(child.$$listenerCount.abc).toEqual(1);
|
|
3068
|
+
|
|
3069
|
+
deregisterEventListener();
|
|
3070
|
+
|
|
3071
|
+
expect($rootScope.$$listenerCount.abc).toEqual(1);
|
|
3072
|
+
expect(child.$$listenerCount.abc).toEqual(1);
|
|
3073
|
+
});
|
|
3074
|
+
});
|
|
3075
|
+
});
|
|
3076
|
+
|
|
3077
|
+
describe("$emit", () => {
|
|
3078
|
+
let log;
|
|
3079
|
+
let child;
|
|
3080
|
+
let grandChild;
|
|
3081
|
+
let greatGrandChild;
|
|
3082
|
+
|
|
3083
|
+
function logger(event) {
|
|
3084
|
+
log += `${event.currentScope.id}>`;
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
beforeEach(() => {
|
|
3088
|
+
log = "";
|
|
3089
|
+
logs = [];
|
|
3090
|
+
child = $rootScope.$new();
|
|
3091
|
+
grandChild = child.$new();
|
|
3092
|
+
greatGrandChild = grandChild.$new();
|
|
3093
|
+
|
|
3094
|
+
$rootScope.id = 0;
|
|
3095
|
+
child.id = 1;
|
|
3096
|
+
grandChild.id = 2;
|
|
3097
|
+
greatGrandChild.id = 3;
|
|
3098
|
+
|
|
3099
|
+
$rootScope.$on("myEvent", logger);
|
|
3100
|
+
child.$on("myEvent", logger);
|
|
3101
|
+
grandChild.$on("myEvent", logger);
|
|
3102
|
+
greatGrandChild.$on("myEvent", logger);
|
|
3103
|
+
});
|
|
3104
|
+
|
|
3105
|
+
it("should bubble event up to the root scope", () => {
|
|
3106
|
+
grandChild.$emit("myEvent");
|
|
3107
|
+
expect(log).toEqual("2>1>0>");
|
|
3108
|
+
});
|
|
3109
|
+
|
|
3110
|
+
it("should allow all events on the same scope to run even if stopPropagation is called", () => {
|
|
3111
|
+
child.$on("myEvent", logger);
|
|
3112
|
+
grandChild.$on("myEvent", (e) => {
|
|
3113
|
+
e.stopPropagation();
|
|
3114
|
+
});
|
|
3115
|
+
grandChild.$on("myEvent", logger);
|
|
3116
|
+
grandChild.$on("myEvent", logger);
|
|
3117
|
+
grandChild.$emit("myEvent");
|
|
3118
|
+
expect(log).toEqual("2>2>2>");
|
|
3119
|
+
});
|
|
3120
|
+
|
|
3121
|
+
it("should dispatch exceptions to the $exceptionHandler", () => {
|
|
3122
|
+
child.$on("myEvent", () => {
|
|
3123
|
+
throw "bubbleException";
|
|
3124
|
+
});
|
|
3125
|
+
grandChild.$emit("myEvent");
|
|
3126
|
+
expect(log).toEqual("2>1>0>");
|
|
3127
|
+
expect(logs).toEqual(["bubbleException"]);
|
|
3128
|
+
});
|
|
3129
|
+
|
|
3130
|
+
it("should allow stopping event propagation", () => {
|
|
3131
|
+
child.$on("myEvent", (event) => {
|
|
3132
|
+
event.stopPropagation();
|
|
3133
|
+
});
|
|
3134
|
+
grandChild.$emit("myEvent");
|
|
3135
|
+
expect(log).toEqual("2>1>");
|
|
3136
|
+
});
|
|
3137
|
+
|
|
3138
|
+
it("should forward method arguments", () => {
|
|
3139
|
+
child.$on("abc", (event, arg1, arg2) => {
|
|
3140
|
+
expect(event.name).toBe("abc");
|
|
3141
|
+
expect(arg1).toBe("arg1");
|
|
3142
|
+
expect(arg2).toBe("arg2");
|
|
3143
|
+
});
|
|
3144
|
+
child.$emit("abc", "arg1", "arg2");
|
|
3145
|
+
});
|
|
3146
|
+
|
|
3147
|
+
it("should allow removing event listener inside a listener on $emit", () => {
|
|
3148
|
+
const spy1 = jasmine.createSpy("1st listener");
|
|
3149
|
+
const spy2 = jasmine.createSpy("2nd listener");
|
|
3150
|
+
const spy3 = jasmine.createSpy("3rd listener");
|
|
3151
|
+
|
|
3152
|
+
const remove1 = child.$on("evt", spy1);
|
|
3153
|
+
const remove2 = child.$on("evt", spy2);
|
|
3154
|
+
const remove3 = child.$on("evt", spy3);
|
|
3155
|
+
|
|
3156
|
+
spy1.and.callFake(remove1);
|
|
3157
|
+
|
|
3158
|
+
expect(child.$$listeners.get("evt").length).toBe(3);
|
|
3159
|
+
|
|
3160
|
+
// should call all listeners and remove 1st
|
|
3161
|
+
child.$emit("evt");
|
|
3162
|
+
expect(spy1).toHaveBeenCalled();
|
|
3163
|
+
expect(spy2).toHaveBeenCalled();
|
|
3164
|
+
expect(spy3).toHaveBeenCalled();
|
|
3165
|
+
expect(child.$$listeners.get("evt").length).toBe(3); // cleanup will happen on next $emit
|
|
3166
|
+
|
|
3167
|
+
spy1.calls.reset();
|
|
3168
|
+
spy2.calls.reset();
|
|
3169
|
+
spy3.calls.reset();
|
|
3170
|
+
|
|
3171
|
+
// should call only 2nd because 1st was already removed and 2nd removes 3rd
|
|
3172
|
+
spy2.and.callFake(remove3);
|
|
3173
|
+
child.$emit("evt");
|
|
3174
|
+
expect(spy1).not.toHaveBeenCalled();
|
|
3175
|
+
expect(spy2).toHaveBeenCalled();
|
|
3176
|
+
expect(spy3).not.toHaveBeenCalled();
|
|
3177
|
+
expect(child.$$listeners.get("evt").length).toBe(1);
|
|
3178
|
+
});
|
|
3179
|
+
|
|
3180
|
+
it("should allow removing event listener inside a listener on $broadcast", () => {
|
|
3181
|
+
const spy1 = jasmine.createSpy("1st listener");
|
|
3182
|
+
const spy2 = jasmine.createSpy("2nd listener");
|
|
3183
|
+
const spy3 = jasmine.createSpy("3rd listener");
|
|
3184
|
+
|
|
3185
|
+
const remove1 = child.$on("evt", spy1);
|
|
3186
|
+
const remove2 = child.$on("evt", spy2);
|
|
3187
|
+
const remove3 = child.$on("evt", spy3);
|
|
3188
|
+
|
|
3189
|
+
spy1.and.callFake(remove1);
|
|
3190
|
+
|
|
3191
|
+
expect(child.$$listeners.get("evt").length).toBe(3);
|
|
3192
|
+
|
|
3193
|
+
// should call all listeners and remove 1st
|
|
3194
|
+
child.$broadcast("evt");
|
|
3195
|
+
expect(spy1).toHaveBeenCalled();
|
|
3196
|
+
expect(spy2).toHaveBeenCalled();
|
|
3197
|
+
expect(spy3).toHaveBeenCalled();
|
|
3198
|
+
expect(child.$$listeners.get("evt").length).toBe(3); // cleanup will happen on next $broadcast
|
|
3199
|
+
|
|
3200
|
+
spy1.calls.reset();
|
|
3201
|
+
spy2.calls.reset();
|
|
3202
|
+
spy3.calls.reset();
|
|
3203
|
+
|
|
3204
|
+
// should call only 2nd because 1st was already removed and 2nd removes 3rd
|
|
3205
|
+
spy2.and.callFake(remove3);
|
|
3206
|
+
child.$broadcast("evt");
|
|
3207
|
+
expect(spy1).not.toHaveBeenCalled();
|
|
3208
|
+
expect(spy2).toHaveBeenCalled();
|
|
3209
|
+
expect(spy3).not.toHaveBeenCalled();
|
|
3210
|
+
expect(child.$$listeners.get("evt").length).toBe(1);
|
|
3211
|
+
});
|
|
3212
|
+
|
|
3213
|
+
describe("event object", () => {
|
|
3214
|
+
it("should have methods/properties", () => {
|
|
3215
|
+
let eventFired = false;
|
|
3216
|
+
|
|
3217
|
+
child.$on("myEvent", (e) => {
|
|
3218
|
+
expect(e.targetScope).toBe(grandChild);
|
|
3219
|
+
expect(e.currentScope).toBe(child);
|
|
3220
|
+
expect(e.name).toBe("myEvent");
|
|
3221
|
+
eventFired = true;
|
|
3222
|
+
});
|
|
3223
|
+
grandChild.$emit("myEvent");
|
|
3224
|
+
expect(eventFired).toBe(true);
|
|
3225
|
+
});
|
|
3226
|
+
|
|
3227
|
+
it("should have its `currentScope` property set to null after emit", () => {
|
|
3228
|
+
let event;
|
|
3229
|
+
|
|
3230
|
+
child.$on("myEvent", (e) => {
|
|
3231
|
+
event = e;
|
|
3232
|
+
});
|
|
3233
|
+
grandChild.$emit("myEvent");
|
|
3234
|
+
|
|
3235
|
+
expect(event.currentScope).toBe(null);
|
|
3236
|
+
expect(event.targetScope).toBe(grandChild);
|
|
3237
|
+
expect(event.name).toBe("myEvent");
|
|
3238
|
+
});
|
|
3239
|
+
|
|
3240
|
+
it("should have preventDefault method and defaultPrevented property", () => {
|
|
3241
|
+
let event = grandChild.$emit("myEvent");
|
|
3242
|
+
expect(event.defaultPrevented).toBe(false);
|
|
3243
|
+
|
|
3244
|
+
child.$on("myEvent", (event) => {
|
|
3245
|
+
event.preventDefault();
|
|
3246
|
+
});
|
|
3247
|
+
event = grandChild.$emit("myEvent");
|
|
3248
|
+
expect(event.defaultPrevented).toBe(true);
|
|
3249
|
+
expect(event.currentScope).toBe(null);
|
|
3250
|
+
});
|
|
3251
|
+
});
|
|
3252
|
+
});
|
|
3253
|
+
|
|
3254
|
+
describe("$broadcast", () => {
|
|
3255
|
+
describe("event propagation", () => {
|
|
3256
|
+
let log;
|
|
3257
|
+
let child1;
|
|
3258
|
+
let child2;
|
|
3259
|
+
let child3;
|
|
3260
|
+
let grandChild11;
|
|
3261
|
+
let grandChild21;
|
|
3262
|
+
let grandChild22;
|
|
3263
|
+
let grandChild23;
|
|
3264
|
+
let greatGrandChild211;
|
|
3265
|
+
|
|
3266
|
+
function logger(event) {
|
|
3267
|
+
log += `${event.currentScope.id}>`;
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
beforeEach(() => {
|
|
3271
|
+
log = "";
|
|
3272
|
+
child1 = $rootScope.$new();
|
|
3273
|
+
child2 = $rootScope.$new();
|
|
3274
|
+
child3 = $rootScope.$new();
|
|
3275
|
+
grandChild11 = child1.$new();
|
|
3276
|
+
grandChild21 = child2.$new();
|
|
3277
|
+
grandChild22 = child2.$new();
|
|
3278
|
+
grandChild23 = child2.$new();
|
|
3279
|
+
greatGrandChild211 = grandChild21.$new();
|
|
3280
|
+
|
|
3281
|
+
$rootScope.id = 0;
|
|
3282
|
+
child1.id = 1;
|
|
3283
|
+
child2.id = 2;
|
|
3284
|
+
child3.id = 3;
|
|
3285
|
+
grandChild11.id = 11;
|
|
3286
|
+
grandChild21.id = 21;
|
|
3287
|
+
grandChild22.id = 22;
|
|
3288
|
+
grandChild23.id = 23;
|
|
3289
|
+
greatGrandChild211.id = 211;
|
|
3290
|
+
|
|
3291
|
+
$rootScope.$on("myEvent", logger);
|
|
3292
|
+
child1.$on("myEvent", logger);
|
|
3293
|
+
child2.$on("myEvent", logger);
|
|
3294
|
+
child3.$on("myEvent", logger);
|
|
3295
|
+
grandChild11.$on("myEvent", logger);
|
|
3296
|
+
grandChild21.$on("myEvent", logger);
|
|
3297
|
+
grandChild22.$on("myEvent", logger);
|
|
3298
|
+
grandChild23.$on("myEvent", logger);
|
|
3299
|
+
greatGrandChild211.$on("myEvent", logger);
|
|
3300
|
+
|
|
3301
|
+
// R
|
|
3302
|
+
// / | \
|
|
3303
|
+
// 1 2 3
|
|
3304
|
+
// / / | \
|
|
3305
|
+
// 11 21 22 23
|
|
3306
|
+
// |
|
|
3307
|
+
// 211
|
|
3308
|
+
});
|
|
3309
|
+
|
|
3310
|
+
it("should broadcast an event from the root scope", () => {
|
|
3311
|
+
$rootScope.$broadcast("myEvent");
|
|
3312
|
+
expect(log).toBe("0>1>11>2>21>211>22>23>3>");
|
|
3313
|
+
});
|
|
3314
|
+
|
|
3315
|
+
it("should broadcast an event from a child scope", () => {
|
|
3316
|
+
child2.$broadcast("myEvent");
|
|
3317
|
+
expect(log).toBe("2>21>211>22>23>");
|
|
3318
|
+
});
|
|
3319
|
+
|
|
3320
|
+
it("should broadcast an event from a leaf scope with a sibling", () => {
|
|
3321
|
+
grandChild22.$broadcast("myEvent");
|
|
3322
|
+
expect(log).toBe("22>");
|
|
3323
|
+
});
|
|
3324
|
+
|
|
3325
|
+
it("should broadcast an event from a leaf scope without a sibling", () => {
|
|
3326
|
+
grandChild23.$broadcast("myEvent");
|
|
3327
|
+
expect(log).toBe("23>");
|
|
3328
|
+
});
|
|
3329
|
+
|
|
3330
|
+
it("should not not fire any listeners for other events", () => {
|
|
3331
|
+
$rootScope.$broadcast("fooEvent");
|
|
3332
|
+
expect(log).toBe("");
|
|
3333
|
+
});
|
|
3334
|
+
|
|
3335
|
+
it("should not descend past scopes with a $$listerCount of 0 or undefined", () => {
|
|
3336
|
+
const EVENT = "fooEvent";
|
|
3337
|
+
const spy = jasmine.createSpy("listener");
|
|
3338
|
+
|
|
3339
|
+
// Precondition: There should be no listeners for fooEvent.
|
|
3340
|
+
expect($rootScope.$$listenerCount[EVENT]).toBeUndefined();
|
|
3341
|
+
|
|
3342
|
+
// Add a spy listener to a child scope.
|
|
3343
|
+
$rootScope.$$childHead.$$listeners[EVENT] = [spy];
|
|
3344
|
+
|
|
3345
|
+
// $rootScope's count for 'fooEvent' is undefined, so spy should not be called.
|
|
3346
|
+
$rootScope.$broadcast(EVENT);
|
|
3347
|
+
expect(spy).not.toHaveBeenCalled();
|
|
3348
|
+
});
|
|
3349
|
+
|
|
3350
|
+
it("should return event object", () => {
|
|
3351
|
+
const result = child1.$broadcast("some");
|
|
3352
|
+
|
|
3353
|
+
expect(result).toBeDefined();
|
|
3354
|
+
expect(result.name).toBe("some");
|
|
3355
|
+
expect(result.targetScope).toBe(child1);
|
|
3356
|
+
});
|
|
3357
|
+
});
|
|
3358
|
+
|
|
3359
|
+
describe("listener", () => {
|
|
3360
|
+
it("should receive event object", () => {
|
|
3361
|
+
const scope = $rootScope;
|
|
3362
|
+
const child = scope.$new();
|
|
3363
|
+
let eventFired = false;
|
|
3364
|
+
|
|
3365
|
+
child.$on("fooEvent", (event) => {
|
|
3366
|
+
eventFired = true;
|
|
3367
|
+
expect(event.name).toBe("fooEvent");
|
|
3368
|
+
expect(event.targetScope).toBe(scope);
|
|
3369
|
+
expect(event.currentScope).toBe(child);
|
|
3370
|
+
});
|
|
3371
|
+
scope.$broadcast("fooEvent");
|
|
3372
|
+
|
|
3373
|
+
expect(eventFired).toBe(true);
|
|
3374
|
+
});
|
|
3375
|
+
|
|
3376
|
+
it("should have the event's `currentScope` property set to null after broadcast", () => {
|
|
3377
|
+
const scope = $rootScope;
|
|
3378
|
+
const child = scope.$new();
|
|
3379
|
+
let event;
|
|
3380
|
+
|
|
3381
|
+
child.$on("fooEvent", (e) => {
|
|
3382
|
+
event = e;
|
|
3383
|
+
});
|
|
3384
|
+
scope.$broadcast("fooEvent");
|
|
3385
|
+
|
|
3386
|
+
expect(event.name).toBe("fooEvent");
|
|
3387
|
+
expect(event.targetScope).toBe(scope);
|
|
3388
|
+
expect(event.currentScope).toBe(null);
|
|
3389
|
+
});
|
|
3390
|
+
|
|
3391
|
+
it("should support passing messages as constargs", () => {
|
|
3392
|
+
const scope = $rootScope;
|
|
3393
|
+
const child = scope.$new();
|
|
3394
|
+
let args;
|
|
3395
|
+
|
|
3396
|
+
child.$on("fooEvent", function () {
|
|
3397
|
+
args = arguments;
|
|
3398
|
+
});
|
|
3399
|
+
scope.$broadcast("fooEvent", "do", "re", "me", "fa");
|
|
3400
|
+
|
|
3401
|
+
expect(args.length).toBe(5);
|
|
3402
|
+
expect(sliceArgs(args, 1)).toEqual(["do", "re", "me", "fa"]);
|
|
3403
|
+
});
|
|
3404
|
+
});
|
|
3405
|
+
});
|
|
3406
|
+
|
|
3407
|
+
it("should allow recursive $emit/$broadcast", () => {
|
|
3408
|
+
let callCount = 0;
|
|
3409
|
+
$rootScope.$on("evt", ($event, arg0) => {
|
|
3410
|
+
callCount++;
|
|
3411
|
+
if (arg0 !== 1234) {
|
|
3412
|
+
$rootScope.$emit("evt", 1234);
|
|
3413
|
+
$rootScope.$broadcast("evt", 1234);
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
|
|
3417
|
+
$rootScope.$emit("evt");
|
|
3418
|
+
$rootScope.$broadcast("evt");
|
|
3419
|
+
expect(callCount).toBe(6);
|
|
3420
|
+
});
|
|
3421
|
+
|
|
3422
|
+
it("should allow recursive $emit/$broadcast between parent/child", () => {
|
|
3423
|
+
const child = $rootScope.$new();
|
|
3424
|
+
let calls = "";
|
|
3425
|
+
|
|
3426
|
+
$rootScope.$on("evt", ($event, arg0) => {
|
|
3427
|
+
calls += "r"; // For "root".
|
|
3428
|
+
if (arg0 === "fromChild") {
|
|
3429
|
+
$rootScope.$broadcast("evt", "fromRoot2");
|
|
3430
|
+
}
|
|
3431
|
+
});
|
|
3432
|
+
|
|
3433
|
+
child.$on("evt", ($event, arg0) => {
|
|
3434
|
+
calls += "c"; // For "child".
|
|
3435
|
+
if (arg0 === "fromRoot1") {
|
|
3436
|
+
child.$emit("evt", "fromChild");
|
|
3437
|
+
}
|
|
3438
|
+
});
|
|
3439
|
+
|
|
3440
|
+
$rootScope.$broadcast("evt", "fromRoot1");
|
|
3441
|
+
expect(calls).toBe("rccrrc");
|
|
3442
|
+
});
|
|
3443
|
+
});
|
|
3444
|
+
|
|
3445
|
+
describe("doc examples", () => {
|
|
3446
|
+
it("should properly fire off watch listeners upon scope changes", () => {
|
|
3447
|
+
// <docs tag="docs1">
|
|
3448
|
+
const scope = $rootScope.$new();
|
|
3449
|
+
scope.salutation = "Hello";
|
|
3450
|
+
scope.name = "World";
|
|
3451
|
+
|
|
3452
|
+
expect(scope.greeting).toEqual(undefined);
|
|
3453
|
+
|
|
3454
|
+
scope.$watch("name", () => {
|
|
3455
|
+
scope.greeting = `${scope.salutation} ${scope.name}!`;
|
|
3456
|
+
}); // initialize the watch
|
|
3457
|
+
|
|
3458
|
+
expect(scope.greeting).toEqual(undefined);
|
|
3459
|
+
scope.name = "Misko";
|
|
3460
|
+
// still old value, since watches have not been called yet
|
|
3461
|
+
expect(scope.greeting).toEqual(undefined);
|
|
3462
|
+
|
|
3463
|
+
scope.$digest(); // fire all the watches
|
|
3464
|
+
expect(scope.greeting).toEqual("Hello Misko!");
|
|
3465
|
+
// </docs>
|
|
3466
|
+
});
|
|
3467
|
+
});
|
|
1533
3468
|
});
|