Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(62)

Unified Diff: third_party/pkg/angular/test/change_detection/watch_group_spec.dart

Issue 257423008: Update all Angular libs (run update_all.sh). (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/pkg/angular/test/change_detection/watch_group_spec.dart
diff --git a/third_party/pkg/angular/test/change_detection/watch_group_spec.dart b/third_party/pkg/angular/test/change_detection/watch_group_spec.dart
index cb13b0ecc48305f0d5efcd0c36bbd3528eb7cc7d..abcdd65d27456b6cd076370579c6e00efa142e97 100644
--- a/third_party/pkg/angular/test/change_detection/watch_group_spec.dart
+++ b/third_party/pkg/angular/test/change_detection/watch_group_spec.dart
@@ -1,646 +1,953 @@
library watch_group_spec;
import '../_specs.dart';
+import 'dart:collection';
import 'package:angular/change_detection/watch_group.dart';
import 'package:angular/change_detection/dirty_checking_change_detector.dart';
+import 'package:angular/change_detection/dirty_checking_change_detector_dynamic.dart';
import 'dirty_checking_change_detector_spec.dart' hide main;
+import 'package:angular/core/parser/parser_dynamic.dart' show DynamicClosureMap;
-main() => describe('WatchGroup', () {
- var context;
- var watchGrp;
- DirtyCheckingChangeDetector changeDetector;
- Logger logger;
+class TestData {
+ sub1(a, {b: 0}) => a - b;
+ sub2({a: 0, b: 0}) => a - b;
+}
- AST parse(String expression) {
- var currentAST = new ContextReferenceAST();
- expression.split('.').forEach((name) {
- currentAST = new FieldReadAST(currentAST, name);
+void main() {
+ describe('WatchGroup', () {
+ var context;
+ var watchGrp;
+ DirtyCheckingChangeDetector changeDetector;
+ Logger logger;
+ Parser parser;
+ ExpressionVisitor visitor;
+
+ beforeEach(inject((Logger _logger, Parser _parser) {
+ context = {};
+ var getterFactory = new DynamicFieldGetterFactory();
+ changeDetector = new DirtyCheckingChangeDetector(getterFactory);
+ watchGrp = new RootWatchGroup(getterFactory, changeDetector, context);
+ visitor = new ExpressionVisitor(new DynamicClosureMap());
+ logger = _logger;
+ parser = _parser;
+ }));
+
+ AST parse(String expression) => visitor.visit(parser(expression));
+
+ eval(String expression, [evalContext]) {
+ AST ast = parse(expression);
+
+ if (evalContext == null) evalContext = context;
+ WatchGroup group = watchGrp.newGroup(evalContext);
+
+ List log = [];
+ Watch watch = group.watch(ast, (v, p) => log.add(v));
+
+ watchGrp.detectChanges();
+ group.remove();
+
+ if (log.isEmpty) {
+ throw new StateError('Expression <$expression> was not evaluated');
+ } else if (log.length > 1) {
+ throw new StateError('Expression <$expression> produced too many values: $log');
+ } else {
+ return log.first;
+ }
+ }
+
+ expectOrder(list) {
+ logger.clear();
+ watchGrp.detectChanges(); // Clear the initial queue
+ logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(list);
+ }
+
+ beforeEach(inject((Logger _logger) {
+ context = {};
+ var getterFactory = new DynamicFieldGetterFactory();
+ changeDetector = new DirtyCheckingChangeDetector(getterFactory);
+ watchGrp = new RootWatchGroup(getterFactory, changeDetector, context);
+ logger = _logger;
+ }));
+
+ describe('watch lifecycle', () {
+ it('should prevent reaction fn on removed', () {
+ context['a'] = 'hello';
+ var watch ;
+ watchGrp.watch(parse('a'), (v, p) {
+ logger('removed');
+ watch.remove();
+ });
+ watch = watchGrp.watch(parse('a'), (v, p) => logger(v));
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['removed']);
+ });
});
- return currentAST;
- }
- expectOrder(list) {
- logger.clear();
- watchGrp.detectChanges(); // Clear the initial queue
- logger.clear();
- watchGrp.detectChanges();
- expect(logger).toEqual(list);
- }
+ describe('property chaining', () {
+ it('should read property', () {
+ context['a'] = 'hello';
+
+ // should fire on initial adding
+ expect(watchGrp.fieldCost).toEqual(0);
+ var watch = watchGrp.watch(parse('a'), (v, p) => logger(v));
+ expect(watch.expression).toEqual('a');
+ expect(watchGrp.fieldCost).toEqual(1);
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello']);
+
+ // make sore no new changes are logged on extra detectChanges
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello']);
- beforeEach(inject((Logger _logger) {
- context = {};
- changeDetector = new DirtyCheckingChangeDetector(new GetterCache({}));
- watchGrp = new RootWatchGroup(changeDetector, context);
- logger = _logger;
- }));
-
- describe('watch lifecycle', () {
- it('should prevent reaction fn on removed', () {
- context['a'] = 'hello';
- var watch ;
- watchGrp.watch(parse('a'), (v, p) {
- logger('removed');
+ // Should detect value change
+ context['a'] = 'bye';
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello', 'bye']);
+
+ // should cleanup after itself
watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ context['a'] = 'cant see me';
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello', 'bye']);
});
- watch = watchGrp.watch(parse('a'), (v, p) => logger(v));
- watchGrp.detectChanges();
- expect(logger).toEqual(['removed']);
- });
- });
- describe('property chaining', () {
- it('should read property', () {
- context['a'] = 'hello';
+ describe('sequence mutations and ref changes', () {
+ it('should handle a simultaneous map mutation and reference change', () {
+ context['a'] = context['b'] = {1: 10, 2: 20};
+ var watchA = watchGrp.watch(new CollectionAST(parse('a')), (v, p) => logger(v));
+ var watchB = watchGrp.watch(new CollectionAST(parse('b')), (v, p) => logger(v));
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(2);
+ expect(logger[0], toEqualMapRecord(
+ map: ['1', '2'],
+ previous: ['1', '2']));
+ expect(logger[1], toEqualMapRecord(
+ map: ['1', '2'],
+ previous: ['1', '2']));
+ logger.clear();
+
+ // context['a'] is set to a copy with an addition.
+ context['a'] = new Map.from(context['a'])..[3] = 30;
+ // context['b'] still has the original collection. We'll mutate it.
+ context['b'].remove(1);
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(2);
+ expect(logger[0], toEqualMapRecord(
+ map: ['1', '2', '3[null -> 30]'],
+ previous: ['1', '2'],
+ additions: ['3[null -> 30]']));
+ expect(logger[1], toEqualMapRecord(
+ map: ['2'],
+ previous: ['1[10 -> null]', '2'],
+ removals: ['1[10 -> null]']));
+ logger.clear();
+ });
+
+ it('should handle a simultaneous list mutation and reference change', () {
+ context['a'] = context['b'] = [0, 1];
+ var watchA = watchGrp.watch(new CollectionAST(parse('a')), (v, p) => logger(v));
+ var watchB = watchGrp.watch(new CollectionAST(parse('b')), (v, p) => logger(v));
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(2);
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['0', '1'],
+ previous: ['0', '1'],
+ additions: [], moves: [], removals: []));
+ expect(logger[1], toEqualCollectionRecord(
+ collection: ['0', '1'],
+ previous: ['0', '1'],
+ additions: [], moves: [], removals: []));
+ logger.clear();
+
+ // context['a'] is set to a copy with an addition.
+ context['a'] = context['a'].toList()..add(2);
+ // context['b'] still has the original collection. We'll mutate it.
+ context['b'].remove(0);
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(2);
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['0', '1', '2[null -> 2]'],
+ previous: ['0', '1'],
+ additions: ['2[null -> 2]'],
+ moves: [],
+ removals: []));
+ expect(logger[1], toEqualCollectionRecord(
+ collection: ['1[1 -> 0]'],
+ previous: ['0[0 -> null]', '1[1 -> 0]'],
+ additions: [],
+ moves: ['1[1 -> 0]'],
+ removals: ['0[0 -> null]']));
+ logger.clear();
+ });
+
+ it('should work correctly with UnmodifiableListView', () {
+ context['a'] = new UnmodifiableListView([0, 1]);
+ var watch = watchGrp.watch(new CollectionAST(parse('a')), (v, p) => logger(v));
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(1);
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['0', '1'],
+ previous: ['0', '1']));
+ logger.clear();
+
+ context['a'] = new UnmodifiableListView([1, 0]);
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(1);
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['1[1 -> 0]', '0[0 -> 1]'],
+ previous: ['0[0 -> 1]', '1[1 -> 0]'],
+ moves: ['1[1 -> 0]', '0[0 -> 1]']));
+ logger.clear();
+ });
- // should fire on initial adding
- expect(watchGrp.fieldCost).toEqual(0);
- var watch = watchGrp.watch(parse('a'), (v, p) => logger(v));
- expect(watch.expression).toEqual('a');
- expect(watchGrp.fieldCost).toEqual(1);
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello']);
+ });
- // make sore no new changes are logged on extra detectChanges
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello']);
+ it('should read property chain', () {
+ context['a'] = {'b': 'hello'};
+
+ // should fire on initial adding
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(changeDetector.count).toEqual(0);
+ var watch = watchGrp.watch(parse('a.b'), (v, p) => logger(v));
+ expect(watch.expression).toEqual('a.b');
+ expect(watchGrp.fieldCost).toEqual(2);
+ expect(changeDetector.count).toEqual(2);
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello']);
+
+ // make sore no new changes are logged on extra detectChanges
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello']);
+
+ // make sure no changes or logged when intermediary object changes
+ context['a'] = {'b': 'hello'};
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello']);
+
+ // Should detect value change
+ context['a'] = {'b': 'hello2'};
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello', 'hello2']);
+
+ // Should detect value change
+ context['a']['b'] = 'bye';
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello', 'hello2', 'bye']);
+
+ // should cleanup after itself
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ context['a']['b'] = 'cant see me';
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['hello', 'hello2', 'bye']);
+ });
- // Should detect value change
- context['a'] = 'bye';
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello', 'bye']);
+ it('should reuse handlers', () {
+ var user1 = {'first': 'misko', 'last': 'hevery'};
+ var user2 = {'first': 'misko', 'last': 'Hevery'};
- // should cleanup after itself
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- context['a'] = 'cant see me';
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello', 'bye']);
- });
+ context['user'] = user1;
- it('should read property chain', () {
- context['a'] = {'b': 'hello'};
+ // should fire on initial adding
+ expect(watchGrp.fieldCost).toEqual(0);
+ var watch = watchGrp.watch(parse('user'), (v, p) => logger(v));
+ var watchFirst = watchGrp.watch(parse('user.first'), (v, p) => logger(v));
+ var watchLast = watchGrp.watch(parse('user.last'), (v, p) => logger(v));
+ expect(watchGrp.fieldCost).toEqual(3);
- // should fire on initial adding
- expect(watchGrp.fieldCost).toEqual(0);
- expect(changeDetector.count).toEqual(0);
- var watch = watchGrp.watch(parse('a.b'), (v, p) => logger(v));
- expect(watch.expression).toEqual('a.b');
- expect(watchGrp.fieldCost).toEqual(2);
- expect(changeDetector.count).toEqual(2);
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello']);
+ watchGrp.detectChanges();
+ expect(logger).toEqual([user1, 'misko', 'hevery']);
+ logger.clear();
- // make sore no new changes are logged on extra detectChanges
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello']);
+ context['user'] = user2;
+ watchGrp.detectChanges();
+ expect(logger).toEqual([user2, 'Hevery']);
- // make sure no changes or logged when intermediary object changes
- context['a'] = {'b': 'hello'};
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello']);
- // Should detect value change
- context['a'] = {'b': 'hello2'};
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello', 'hello2']);
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(3);
- // Should detect value change
- context['a']['b'] = 'bye';
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello', 'hello2', 'bye']);
+ watchFirst.remove();
+ expect(watchGrp.fieldCost).toEqual(2);
- // should cleanup after itself
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- context['a']['b'] = 'cant see me';
- watchGrp.detectChanges();
- expect(logger).toEqual(['hello', 'hello2', 'bye']);
- });
+ watchLast.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
- it('should reuse handlers', () {
- var user1 = {'first': 'misko', 'last': 'hevery'};
- var user2 = {'first': 'misko', 'last': 'Hevery'};
+ expect(() => watch.remove()).toThrow('Already deleted!');
+ });
- context['user'] = user1;
+ it('should eval pure FunctionApply', () {
+ context['a'] = {'val': 1};
- // should fire on initial adding
- expect(watchGrp.fieldCost).toEqual(0);
- var watch = watchGrp.watch(parse('user'), (v, p) => logger(v));
- var watchFirst = watchGrp.watch(parse('user.first'), (v, p) => logger(v));
- var watchLast = watchGrp.watch(parse('user.last'), (v, p) => logger(v));
- expect(watchGrp.fieldCost).toEqual(3);
+ FunctionApply fn = new LoggingFunctionApply(logger);
+ var watch = watchGrp.watch(
+ new PureFunctionAST('add', fn, [parse('a.val')]),
+ (v, p) => logger(v)
+ );
- watchGrp.detectChanges();
- expect(logger).toEqual([user1, 'misko', 'hevery']);
- logger.clear();
+ // a; a.val; b; b.val;
+ expect(watchGrp.fieldCost).toEqual(2);
+ // add
+ expect(watchGrp.evalCost).toEqual(1);
- context['user'] = user2;
- watchGrp.detectChanges();
- expect(logger).toEqual([user2, 'Hevery']);
+ watchGrp.detectChanges();
+ expect(logger).toEqual([[1], null]);
+ logger.clear();
+ context['a'] = {'val': 2};
+ watchGrp.detectChanges();
+ expect(logger).toEqual([[2]]);
+ });
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(3);
- watchFirst.remove();
- expect(watchGrp.fieldCost).toEqual(2);
+ it('should eval pure function', () {
+ context['a'] = {'val': 1};
+ context['b'] = {'val': 2};
- watchLast.remove();
- expect(watchGrp.fieldCost).toEqual(0);
+ var watch = watchGrp.watch(
+ new PureFunctionAST('add',
+ (a, b) { logger('+'); return a+b; },
+ [parse('a.val'), parse('b.val')]
+ ),
+ (v, p) => logger(v)
+ );
- expect(() => watch.remove()).toThrow('Already deleted!');
- });
+ // a; a.val; b; b.val;
+ expect(watchGrp.fieldCost).toEqual(4);
+ // add
+ expect(watchGrp.evalCost).toEqual(1);
- it('should eval pure FunctionApply', () {
- context['a'] = {'val': 1};
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 3]);
- FunctionApply fn = new LoggingFunctionApply(logger);
- var watch = watchGrp.watch(
- new PureFunctionAST('add', fn, [parse('a.val')]),
- (v, p) => logger(v)
- );
+ // extra checks should not trigger functions
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 3]);
- // a; a.val; b; b.val;
- expect(watchGrp.fieldCost).toEqual(2);
- // add
- expect(watchGrp.evalCost).toEqual(1);
+ // multiple arg changes should only trigger function once.
+ context['a']['val'] = 3;
+ context['b']['val'] = 4;
- watchGrp.detectChanges();
- expect(logger).toEqual([[1], null]);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 3, '+', 7]);
- context['a'] = {'val': 2};
- watchGrp.detectChanges();
- expect(logger).toEqual([[2]]);
- });
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
+ context['a']['val'] = 0;
+ context['b']['val'] = 0;
- it('should eval pure function', () {
- context['a'] = {'val': 1};
- context['b'] = {'val': 2};
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 3, '+', 7]);
+ });
- var watch = watchGrp.watch(
- new PureFunctionAST('add',
- (a, b) { logger('+'); return a+b; },
- [parse('a.val'), parse('b.val')]
- ),
- (v, p) => logger(v)
- );
- // a; a.val; b; b.val;
- expect(watchGrp.fieldCost).toEqual(4);
- // add
- expect(watchGrp.evalCost).toEqual(1);
+ it('should eval closure', () {
+ context['a'] = {'val': 1};
+ context['b'] = {'val': 2};
+ var innerState = 1;
- watchGrp.detectChanges();
- expect(logger).toEqual(['+', 3]);
+ var watch = watchGrp.watch(
+ new ClosureAST('sum',
+ (a, b) { logger('+'); return innerState+a+b; },
+ [parse('a.val'), parse('b.val')]
+ ),
+ (v, p) => logger(v)
+ );
- // extra checks should not trigger functions
- watchGrp.detectChanges();
- watchGrp.detectChanges();
- expect(logger).toEqual(['+', 3]);
+ // a; a.val; b; b.val;
+ expect(watchGrp.fieldCost).toEqual(4);
+ // add
+ expect(watchGrp.evalCost).toEqual(1);
- // multiple arg changes should only trigger function once.
- context['a']['val'] = 3;
- context['b']['val'] = 4;
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 4]);
- watchGrp.detectChanges();
- expect(logger).toEqual(['+', 3, '+', 7]);
+ // extra checks should trigger closures
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 4, '+', '+']);
+ logger.clear();
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- expect(watchGrp.evalCost).toEqual(0);
+ // multiple arg changes should only trigger function once.
+ context['a']['val'] = 3;
+ context['b']['val'] = 4;
- context['a']['val'] = 0;
- context['b']['val'] = 0;
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 8]);
+ logger.clear();
- watchGrp.detectChanges();
- expect(logger).toEqual(['+', 3, '+', 7]);
- });
+ // inner state change should only trigger function once.
+ innerState = 2;
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['+', 9]);
+ logger.clear();
- it('should eval chained pure function', () {
- context['a'] = {'val': 1};
- context['b'] = {'val': 2};
- context['c'] = {'val': 3};
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
- var a_plus_b = new PureFunctionAST('add1',
- (a, b) { logger('$a+$b'); return a + b; },
- [parse('a.val'), parse('b.val')]);
+ context['a']['val'] = 0;
+ context['b']['val'] = 0;
- var a_plus_b_plus_c = new PureFunctionAST('add2',
- (b, c) { logger('$b+$c'); return b + c; },
- [a_plus_b, parse('c.val')]);
+ watchGrp.detectChanges();
+ expect(logger).toEqual([]);
+ });
- var watch = watchGrp.watch(a_plus_b_plus_c, (v, p) => logger(v));
- // a; a.val; b; b.val; c; c.val;
- expect(watchGrp.fieldCost).toEqual(6);
- // add
- expect(watchGrp.evalCost).toEqual(2);
+ it('should eval chained pure function', () {
+ context['a'] = {'val': 1};
+ context['b'] = {'val': 2};
+ context['c'] = {'val': 3};
- watchGrp.detectChanges();
- expect(logger).toEqual(['1+2', '3+3', 6]);
- logger.clear();
+ var a_plus_b = new PureFunctionAST('add1',
+ (a, b) { logger('$a+$b'); return a + b; },
+ [parse('a.val'), parse('b.val')]);
- // extra checks should not trigger functions
- watchGrp.detectChanges();
- watchGrp.detectChanges();
- expect(logger).toEqual([]);
- logger.clear();
+ var a_plus_b_plus_c = new PureFunctionAST('add2',
+ (b, c) { logger('$b+$c'); return b + c; },
+ [a_plus_b, parse('c.val')]);
- // multiple arg changes should only trigger function once.
- context['a']['val'] = 3;
- context['b']['val'] = 4;
- context['c']['val'] = 5;
- watchGrp.detectChanges();
- expect(logger).toEqual(['3+4', '7+5', 12]);
- logger.clear();
+ var watch = watchGrp.watch(a_plus_b_plus_c, (v, p) => logger(v));
- context['a']['val'] = 9;
- watchGrp.detectChanges();
- expect(logger).toEqual(['9+4', '13+5', 18]);
- logger.clear();
+ // a; a.val; b; b.val; c; c.val;
+ expect(watchGrp.fieldCost).toEqual(6);
+ // add
+ expect(watchGrp.evalCost).toEqual(2);
- context['c']['val'] = 9;
- watchGrp.detectChanges();
- expect(logger).toEqual(['13+9', 22]);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['1+2', '3+3', 6]);
+ logger.clear();
+ // extra checks should not trigger functions
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual([]);
+ logger.clear();
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- expect(watchGrp.evalCost).toEqual(0);
+ // multiple arg changes should only trigger function once.
+ context['a']['val'] = 3;
+ context['b']['val'] = 4;
+ context['c']['val'] = 5;
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['3+4', '7+5', 12]);
+ logger.clear();
- context['a']['val'] = 0;
- context['b']['val'] = 0;
+ context['a']['val'] = 9;
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['9+4', '13+5', 18]);
+ logger.clear();
- watchGrp.detectChanges();
- expect(logger).toEqual([]);
- });
+ context['c']['val'] = 9;
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['13+9', 22]);
+ logger.clear();
- it('should eval closure', () {
- var obj;
- obj = {
- 'methodA': (arg1) {
- logger('methodA($arg1) => ${obj['valA']}');
- return obj['valA'];
- },
- 'valA': 'A'
- };
- context['obj'] = obj;
- context['arg0'] = 1;
-
- var watch = watchGrp.watch(
- new MethodAST(parse('obj'), 'methodA', [parse('arg0')]),
- (v, p) => logger(v)
- );
-
- // obj, arg0;
- expect(watchGrp.fieldCost).toEqual(2);
- // methodA()
- expect(watchGrp.evalCost).toEqual(1);
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(1) => A', 'A']);
- logger.clear();
+ context['a']['val'] = 0;
+ context['b']['val'] = 0;
- watchGrp.detectChanges();
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual([]);
+ });
- obj['valA'] = 'B';
- context['arg0'] = 2;
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(2) => B', 'B']);
- logger.clear();
+ it('should eval closure', () {
+ var obj;
+ obj = {
+ 'methodA': (arg1) {
+ logger('methodA($arg1) => ${obj['valA']}');
+ return obj['valA'];
+ },
+ 'valA': 'A'
+ };
+ context['obj'] = obj;
+ context['arg0'] = 1;
+
+ var watch = watchGrp.watch(
+ new MethodAST(parse('obj'), 'methodA', [parse('arg0')]),
+ (v, p) => logger(v)
+ );
+
+ // obj, arg0;
+ expect(watchGrp.fieldCost).toEqual(2);
+ // methodA()
+ expect(watchGrp.evalCost).toEqual(1);
+
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(1) => A', 'A']);
+ logger.clear();
+
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']);
+ logger.clear();
+
+ obj['valA'] = 'B';
+ context['arg0'] = 2;
+
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(2) => B', 'B']);
+ logger.clear();
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- expect(watchGrp.evalCost).toEqual(0);
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
- obj['valA'] = 'C';
- context['arg0'] = 3;
+ obj['valA'] = 'C';
+ context['arg0'] = 3;
- watchGrp.detectChanges();
- expect(logger).toEqual([]);
- });
+ watchGrp.detectChanges();
+ expect(logger).toEqual([]);
+ });
- it('should eval method', () {
- var obj = new MyClass(logger);
- obj.valA = 'A';
- context['obj'] = obj;
- context['arg0'] = 1;
+ it('should eval method', () {
+ var obj = new MyClass(logger);
+ obj.valA = 'A';
+ context['obj'] = obj;
+ context['arg0'] = 1;
- var watch = watchGrp.watch(
- new MethodAST(parse('obj'), 'methodA', [parse('arg0')]),
- (v, p) => logger(v)
- );
+ var watch = watchGrp.watch(
+ new MethodAST(parse('obj'), 'methodA', [parse('arg0')]),
+ (v, p) => logger(v)
+ );
- // obj, arg0;
- expect(watchGrp.fieldCost).toEqual(2);
- // methodA()
- expect(watchGrp.evalCost).toEqual(1);
+ // obj, arg0;
+ expect(watchGrp.fieldCost).toEqual(2);
+ // methodA()
+ expect(watchGrp.evalCost).toEqual(1);
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(1) => A', 'A']);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(1) => A', 'A']);
+ logger.clear();
- watchGrp.detectChanges();
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']);
- logger.clear();
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(1) => A', 'methodA(1) => A']);
+ logger.clear();
- obj.valA = 'B';
- context['arg0'] = 2;
+ obj.valA = 'B';
+ context['arg0'] = 2;
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(2) => B', 'B']);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(2) => B', 'B']);
+ logger.clear();
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- expect(watchGrp.evalCost).toEqual(0);
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
- obj.valA = 'C';
- context['arg0'] = 3;
+ obj.valA = 'C';
+ context['arg0'] = 3;
- watchGrp.detectChanges();
- expect(logger).toEqual([]);
- });
+ watchGrp.detectChanges();
+ expect(logger).toEqual([]);
+ });
- it('should eval method chain', () {
- var obj1 = new MyClass(logger);
- var obj2 = new MyClass(logger);
- obj1.valA = obj2;
- obj2.valA = 'A';
- context['obj'] = obj1;
- context['arg0'] = 0;
- context['arg1'] = 1;
-
- // obj.methodA(arg0)
- var ast = new MethodAST(parse('obj'), 'methodA', [parse('arg0')]);
- ast = new MethodAST(ast, 'methodA', [parse('arg1')]);
- var watch = watchGrp.watch(ast, (v, p) => logger(v));
-
- // obj, arg0, arg1;
- expect(watchGrp.fieldCost).toEqual(3);
- // methodA(), mothodA()
- expect(watchGrp.evalCost).toEqual(2);
+ it('should eval method chain', () {
+ var obj1 = new MyClass(logger);
+ var obj2 = new MyClass(logger);
+ obj1.valA = obj2;
+ obj2.valA = 'A';
+ context['obj'] = obj1;
+ context['arg0'] = 0;
+ context['arg1'] = 1;
+
+ // obj.methodA(arg0)
+ var ast = new MethodAST(parse('obj'), 'methodA', [parse('arg0')]);
+ ast = new MethodAST(ast, 'methodA', [parse('arg1')]);
+ var watch = watchGrp.watch(ast, (v, p) => logger(v));
+
+ // obj, arg0, arg1;
+ expect(watchGrp.fieldCost).toEqual(3);
+ // methodA(), methodA()
+ expect(watchGrp.evalCost).toEqual(2);
+
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A', 'A']);
+ logger.clear();
+
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A',
+ 'methodA(0) => MyClass', 'methodA(1) => A']);
+ logger.clear();
+
+ obj2.valA = 'B';
+ context['arg0'] = 10;
+ context['arg1'] = 11;
+
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['methodA(10) => MyClass', 'methodA(11) => B', 'B']);
+ logger.clear();
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A', 'A']);
- logger.clear();
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
- watchGrp.detectChanges();
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(0) => MyClass', 'methodA(1) => A',
- 'methodA(0) => MyClass', 'methodA(1) => A']);
- logger.clear();
+ obj2.valA = 'C';
+ context['arg0'] = 20;
+ context['arg1'] = 21;
+
+ watchGrp.detectChanges();
+ expect(logger).toEqual([]);
+ });
- obj2.valA = 'B';
- context['arg0'] = 10;
- context['arg1'] = 11;
+ it('should not return null when evaling method first time', () {
+ context['text'] ='abc';
+ var ast = new MethodAST(parse('text'), 'toUpperCase', []);
+ var watch = watchGrp.watch(ast, (v, p) => logger(v));
- watchGrp.detectChanges();
- expect(logger).toEqual(['methodA(10) => MyClass', 'methodA(11) => B', 'B']);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['ABC']);
+ });
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- expect(watchGrp.evalCost).toEqual(0);
+ it('should not eval a function if registered during reaction', () {
+ context['text'] ='abc';
+ var ast = new MethodAST(parse('text'), 'toLowerCase', []);
+ var watch = watchGrp.watch(ast, (v, p) {
+ var ast = new MethodAST(parse('text'), 'toUpperCase', []);
+ watchGrp.watch(ast, (v, p) {
+ logger(v);
+ });
+ });
+
+ watchGrp.detectChanges();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['ABC']);
+ });
- obj2.valA = 'C';
- context['arg0'] = 20;
- context['arg1'] = 21;
- watchGrp.detectChanges();
- expect(logger).toEqual([]);
- });
+ it('should eval function eagerly when registered during reaction', () {
+ var fn = (arg) { logger('fn($arg)'); return arg; };
+ context['obj'] = {'fn': fn};
+ context['arg1'] = 'OUT';
+ context['arg2'] = 'IN';
+ var ast = new MethodAST(parse('obj'), 'fn', [parse('arg1')]);
+ var watch = watchGrp.watch(ast, (v, p) {
+ var ast = new MethodAST(parse('obj'), 'fn', [parse('arg2')]);
+ watchGrp.watch(ast, (v, p) {
+ logger('reaction: $v');
+ });
+ });
+
+ expect(logger).toEqual([]);
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['fn(OUT)', 'fn(IN)', 'reaction: IN']);
+ logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['fn(OUT)', 'fn(IN)']);
+ });
- it('should read connstant', () {
- // should fire on initial adding
- expect(watchGrp.fieldCost).toEqual(0);
- var watch = watchGrp.watch(new ConstantAST(123), (v, p) => logger(v));
- expect(watch.expression).toEqual('123');
- expect(watchGrp.fieldCost).toEqual(0);
- watchGrp.detectChanges();
- expect(logger).toEqual([123]);
- // make sore no new changes are logged on extra detectChanges
- watchGrp.detectChanges();
- expect(logger).toEqual([123]);
- });
+ it('should read constant', () {
+ // should fire on initial adding
+ expect(watchGrp.fieldCost).toEqual(0);
+ var watch = watchGrp.watch(new ConstantAST(123), (v, p) => logger(v));
+ expect(watch.expression).toEqual('123');
+ expect(watchGrp.fieldCost).toEqual(0);
+ watchGrp.detectChanges();
+ expect(logger).toEqual([123]);
- it('should wrap iterable in ObservableList', () {
- context['list'] = [];
- var watch = watchGrp.watch(new CollectionAST(parse('list')), (v, p) => logger(v));
+ // make sore no new changes are logged on extra detectChanges
+ watchGrp.detectChanges();
+ expect(logger).toEqual([123]);
+ });
- expect(watchGrp.fieldCost).toEqual(1);
- expect(watchGrp.collectionCost).toEqual(1);
- expect(watchGrp.evalCost).toEqual(0);
+ it('should wrap iterable in ObservableList', () {
+ context['list'] = [];
+ var watch = watchGrp.watch(new CollectionAST(parse('list')), (v, p) => logger(v));
+
+ expect(watchGrp.fieldCost).toEqual(1);
+ expect(watchGrp.collectionCost).toEqual(1);
+ expect(watchGrp.evalCost).toEqual(0);
+
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(1);
+ expect(logger[0], toEqualCollectionRecord(
+ collection: [],
+ additions: [],
+ moves: [],
+ removals: []));
+ logger.clear();
+
+ context['list'] = [1];
+ watchGrp.detectChanges();
+ expect(logger.length).toEqual(1);
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['1[null -> 0]'],
+ additions: ['1[null -> 0]'],
+ moves: [],
+ removals: []));
+ logger.clear();
- watchGrp.detectChanges();
- expect(logger.length).toEqual(1);
- expect(logger[0], toEqualCollectionRecord(
- collection: [],
- additions: [],
- moves: [],
- removals: []));
- logger.clear();
+ watch.remove();
+ expect(watchGrp.fieldCost).toEqual(0);
+ expect(watchGrp.collectionCost).toEqual(0);
+ expect(watchGrp.evalCost).toEqual(0);
+ });
- context['list'] = [1];
- watchGrp.detectChanges();
- expect(logger.length).toEqual(1);
- expect(logger[0], toEqualCollectionRecord(
- collection: ['1[null -> 0]'],
- additions: ['1[null -> 0]'],
- moves: [],
- removals: []));
- logger.clear();
+ it('should watch literal arrays made of expressions', () {
+ context['a'] = 1;
+ var ast = new CollectionAST(
+ new PureFunctionAST('[a]', new ArrayFn(), [parse('a')])
+ );
+ var watch = watchGrp.watch(ast, (v, p) => logger(v));
+ watchGrp.detectChanges();
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['1[null -> 0]'],
+ additions: ['1[null -> 0]'],
+ moves: [],
+ removals: []));
+ logger.clear();
+
+ context['a'] = 2;
+ watchGrp.detectChanges();
+ expect(logger[0], toEqualCollectionRecord(
+ collection: ['2[null -> 0]'],
+ previous: ['1[0 -> null]'],
+ additions: ['2[null -> 0]'],
+ moves: [],
+ removals: ['1[0 -> null]']));
+ logger.clear();
+ });
- watch.remove();
- expect(watchGrp.fieldCost).toEqual(0);
- expect(watchGrp.collectionCost).toEqual(0);
- expect(watchGrp.evalCost).toEqual(0);
+ it('should watch pure function whose result goes to pure function', () {
+ context['a'] = 1;
+ var ast = new PureFunctionAST(
+ '-',
+ (v) => -v,
+ [new PureFunctionAST('++', (v) => v + 1, [parse('a')])]
+ );
+ var watch = watchGrp.watch(ast, (v, p) => logger(v));
+
+ expect(watchGrp.detectChanges()).not.toBe(null);
+ expect(logger).toEqual([-2]);
+ logger.clear();
+
+ context['a'] = 2;
+ expect(watchGrp.detectChanges()).not.toBe(null);
+ expect(logger).toEqual([-3]);
+ });
});
- it('should watch literal arrays made of expressions', () {
- context['a'] = 1;
- var ast = new CollectionAST(
- new PureFunctionAST('[a]', new ArrayFn(), [parse('a')])
- );
- var watch = watchGrp.watch(ast, (v, p) => logger(v));
- watchGrp.detectChanges();
- expect(logger[0], toEqualCollectionRecord(
- collection: ['1[null -> 0]'],
- additions: ['1[null -> 0]'],
- moves: [],
- removals: []));
- logger.clear();
+ describe('evaluation', () {
+ it('should support simple literals', () {
+ expect(eval('42')).toBe(42);
+ expect(eval('87')).toBe(87);
+ });
- context['a'] = 2;
- watchGrp.detectChanges();
- expect(logger[0], toEqualCollectionRecord(
- collection: ['2[null -> 0]'],
- additions: ['2[null -> 0]'],
- moves: [],
- removals: ['1[0 -> null]']));
- logger.clear();
- });
+ it('should support context access', () {
+ context['x'] = 42;
+ expect(eval('x')).toBe(42);
+ context['y'] = 87;
+ expect(eval('y')).toBe(87);
+ });
- it('should watch pure function whose result goes to pure function', () {
- context['a'] = 1;
- var ast = new PureFunctionAST(
- '-',
- (v) => -v,
- [new PureFunctionAST('++', (v) => v + 1, [parse('a')])]
- );
- var watch = watchGrp.watch(ast, (v, p) => logger(v));
-
- expect(watchGrp.detectChanges()).not.toBe(null);
- expect(logger).toEqual([-2]);
- logger.clear();
+ it('should support custom context', () {
+ expect(eval('x', {'x': 42})).toBe(42);
+ expect(eval('x', {'x': 87})).toBe(87);
+ });
- context['a'] = 2;
- expect(watchGrp.detectChanges()).not.toBe(null);
- expect(logger).toEqual([-3]);
- });
- });
+ it('should support named arguments for scope calls', () {
+ var data = new TestData();
+ expect(eval("sub1(1)", data)).toEqual(1);
+ expect(eval("sub1(3, b: 2)", data)).toEqual(1);
- describe('child group', () {
- it('should remove all field watches in group and group\'s children', () {
- watchGrp.watch(parse('a'), (v, p) => logger('0a'));
- var child1a = watchGrp.newGroup(new PrototypeMap(context));
- var child1b = watchGrp.newGroup(new PrototypeMap(context));
- var child2 = child1a.newGroup(new PrototypeMap(context));
- child1a.watch(parse('a'), (v, p) => logger('1a'));
- child1b.watch(parse('a'), (v, p) => logger('1b'));
- watchGrp.watch(parse('a'), (v, p) => logger('0A'));
- child1a.watch(parse('a'), (v, p) => logger('1A'));
- child2.watch(parse('a'), (v, p) => logger('2A'));
-
- // flush initial reaction functions
- expect(watchGrp.detectChanges()).toEqual(6);
- // expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']);
- expect(logger).toEqual(['0a', '1a', '1b', '0A', '1A', '2A']); // we go by registration order
- expect(watchGrp.fieldCost).toEqual(1);
- expect(watchGrp.totalFieldCost).toEqual(4);
- logger.clear();
+ expect(eval("sub2()", data)).toEqual(0);
+ expect(eval("sub2(a: 3)", data)).toEqual(3);
+ expect(eval("sub2(a: 3, b: 2)", data)).toEqual(1);
+ expect(eval("sub2(b: 4)", data)).toEqual(-4);
+ });
- context['a'] = 1;
- expect(watchGrp.detectChanges()).toEqual(6);
- expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']); // we go by group order
- logger.clear();
+ it('should support named arguments for scope calls (map)', () {
+ context["sub1"] = (a, {b: 0}) => a - b;
+ expect(eval("sub1(1)")).toEqual(1);
+ expect(eval("sub1(3, b: 2)")).toEqual(1);
- context['a'] = 2;
- child1a.remove(); // should also remove child2
- expect(watchGrp.detectChanges()).toEqual(3);
- expect(logger).toEqual(['0a', '0A', '1b']);
- expect(watchGrp.fieldCost).toEqual(1);
- expect(watchGrp.totalFieldCost).toEqual(2);
- });
+ context["sub2"] = ({a: 0, b: 0}) => a - b;
+ expect(eval("sub2()")).toEqual(0);
+ expect(eval("sub2(a: 3)")).toEqual(3);
+ expect(eval("sub2(a: 3, b: 2)")).toEqual(1);
+ expect(eval("sub2(b: 4)")).toEqual(-4);
+ });
- it('should remove all method watches in group and group\'s children', () {
- context['my'] = new MyClass(logger);
- AST countMethod = new MethodAST(parse('my'), 'count', []);
- watchGrp.watch(countMethod, (v, p) => logger('0a'));
- expectOrder(['0a']);
-
- var child1a = watchGrp.newGroup(new PrototypeMap(context));
- var child1b = watchGrp.newGroup(new PrototypeMap(context));
- var child2 = child1a.newGroup(new PrototypeMap(context));
- child1a.watch(countMethod, (v, p) => logger('1a'));
- expectOrder(['0a', '1a']);
- child1b.watch(countMethod, (v, p) => logger('1b'));
- expectOrder(['0a', '1a', '1b']);
- watchGrp.watch(countMethod, (v, p) => logger('0A'));
- expectOrder(['0a', '0A', '1a', '1b']);
- child1a.watch(countMethod, (v, p) => logger('1A'));
- expectOrder(['0a', '0A', '1a', '1A', '1b']);
- child2.watch(countMethod, (v, p) => logger('2A'));
- expectOrder(['0a', '0A', '1a', '1A', '2A', '1b']);
-
- // flush initial reaction functions
- expect(watchGrp.detectChanges()).toEqual(6);
- expectOrder(['0a', '0A', '1a', '1A', '2A', '1b']);
-
- child1a.remove(); // should also remove child2
- expect(watchGrp.detectChanges()).toEqual(3);
- expectOrder(['0a', '0A', '1b']);
+ it('should support named arguments for member calls', () {
+ context['o'] = new TestData();
+ expect(eval("o.sub1(1)")).toEqual(1);
+ expect(eval("o.sub1(3, b: 2)")).toEqual(1);
+
+ expect(eval("o.sub2()")).toEqual(0);
+ expect(eval("o.sub2(a: 3)")).toEqual(3);
+ expect(eval("o.sub2(a: 3, b: 2)")).toEqual(1);
+ expect(eval("o.sub2(b: 4)")).toEqual(-4);
+ });
+
+ it('should support named arguments for member calls (map)', () {
+ context['o'] = {
+ 'sub1': (a, {b: 0}) => a - b,
+ 'sub2': ({a: 0, b: 0}) => a - b
+ };
+ expect(eval("o.sub1(1)")).toEqual(1);
+ expect(eval("o.sub1(3, b: 2)")).toEqual(1);
+
+ expect(eval("o.sub2()")).toEqual(0);
+ expect(eval("o.sub2(a: 3)")).toEqual(3);
+ expect(eval("o.sub2(a: 3, b: 2)")).toEqual(1);
+ expect(eval("o.sub2(b: 4)")).toEqual(-4);
+ });
});
- it('should add watches within its own group', () {
- context['my'] = new MyClass(logger);
- AST countMethod = new MethodAST(parse('my'), 'count', []);
- var ra = watchGrp.watch(countMethod, (v, p) => logger('a'));
- var child = watchGrp.newGroup(new PrototypeMap(context));
- var cb = child.watch(countMethod, (v, p) => logger('b'));
+ describe('child group', () {
+ it('should remove all field watches in group and group\'s children', () {
+ watchGrp.watch(parse('a'), (v, p) => logger('0a'));
+ var child1a = watchGrp.newGroup(new PrototypeMap(context));
+ var child1b = watchGrp.newGroup(new PrototypeMap(context));
+ var child2 = child1a.newGroup(new PrototypeMap(context));
+ child1a.watch(parse('a'), (v, p) => logger('1a'));
+ child1b.watch(parse('a'), (v, p) => logger('1b'));
+ watchGrp.watch(parse('a'), (v, p) => logger('0A'));
+ child1a.watch(parse('a'), (v, p) => logger('1A'));
+ child2.watch(parse('a'), (v, p) => logger('2A'));
+
+ // flush initial reaction functions
+ expect(watchGrp.detectChanges()).toEqual(6);
+ // expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']);
+ expect(logger).toEqual(['0a', '1a', '1b', '0A', '1A', '2A']); // we go by registration order
+ expect(watchGrp.fieldCost).toEqual(1);
+ expect(watchGrp.totalFieldCost).toEqual(4);
+ logger.clear();
+
+ context['a'] = 1;
+ expect(watchGrp.detectChanges()).toEqual(6);
+ expect(logger).toEqual(['0a', '0A', '1a', '1A', '2A', '1b']); // we go by group order
+ logger.clear();
+
+ context['a'] = 2;
+ child1a.remove(); // should also remove child2
+ expect(watchGrp.detectChanges()).toEqual(3);
+ expect(logger).toEqual(['0a', '0A', '1b']);
+ expect(watchGrp.fieldCost).toEqual(1);
+ expect(watchGrp.totalFieldCost).toEqual(2);
+ });
- expectOrder(['a', 'b']);
- expectOrder(['a', 'b']);
+ it('should remove all method watches in group and group\'s children', () {
+ context['my'] = new MyClass(logger);
+ AST countMethod = new MethodAST(parse('my'), 'count', []);
+ watchGrp.watch(countMethod, (v, p) => logger('0a'));
+ expectOrder(['0a']);
+
+ var child1a = watchGrp.newGroup(new PrototypeMap(context));
+ var child1b = watchGrp.newGroup(new PrototypeMap(context));
+ var child2 = child1a.newGroup(new PrototypeMap(context));
+ var child3 = child2.newGroup(new PrototypeMap(context));
+ child1a.watch(countMethod, (v, p) => logger('1a'));
+ expectOrder(['0a', '1a']);
+ child1b.watch(countMethod, (v, p) => logger('1b'));
+ expectOrder(['0a', '1a', '1b']);
+ watchGrp.watch(countMethod, (v, p) => logger('0A'));
+ expectOrder(['0a', '0A', '1a', '1b']);
+ child1a.watch(countMethod, (v, p) => logger('1A'));
+ expectOrder(['0a', '0A', '1a', '1A', '1b']);
+ child2.watch(countMethod, (v, p) => logger('2A'));
+ expectOrder(['0a', '0A', '1a', '1A', '2A', '1b']);
+ child3.watch(countMethod, (v, p) => logger('3'));
+ expectOrder(['0a', '0A', '1a', '1A', '2A', '3', '1b']);
+
+ // flush initial reaction functions
+ expect(watchGrp.detectChanges()).toEqual(7);
+ expectOrder(['0a', '0A', '1a', '1A', '2A', '3', '1b']);
+
+ child1a.remove(); // should also remove child2 and child 3
+ expect(watchGrp.detectChanges()).toEqual(3);
+ expectOrder(['0a', '0A', '1b']);
+ });
- ra.remove();
- expectOrder(['b']);
+ it('should add watches within its own group', () {
+ context['my'] = new MyClass(logger);
+ AST countMethod = new MethodAST(parse('my'), 'count', []);
+ var ra = watchGrp.watch(countMethod, (v, p) => logger('a'));
+ var child = watchGrp.newGroup(new PrototypeMap(context));
+ var cb = child.watch(countMethod, (v, p) => logger('b'));
- cb.remove();
- expectOrder([]);
+ expectOrder(['a', 'b']);
+ expectOrder(['a', 'b']);
- // TODO: add them back in wrong order, assert events in right order
- cb = child.watch(countMethod, (v, p) => logger('b'));
- ra = watchGrp.watch(countMethod, (v, p) => logger('a'));;
- expectOrder(['a', 'b']);
- });
+ ra.remove();
+ expectOrder(['b']);
+ cb.remove();
+ expectOrder([]);
- it('should not call reaction function on removed group', () {
- var log = [];
- context['name'] = 'misko';
- var child = watchGrp.newGroup(context);
- watchGrp.watch(parse('name'), (v, _) {
- log.add('root $v');
- if (v == 'destroy') {
- child.remove();
- }
+ // TODO: add them back in wrong order, assert events in right order
+ cb = child.watch(countMethod, (v, p) => logger('b'));
+ ra = watchGrp.watch(countMethod, (v, p) => logger('a'));;
+ expectOrder(['a', 'b']);
});
- child.watch(parse('name'), (v, _) => log.add('child $v'));
- watchGrp.detectChanges();
- expect(log).toEqual(['root misko', 'child misko']);
- log.clear();
- context['name'] = 'destroy';
- watchGrp.detectChanges();
- expect(log).toEqual(['root destroy']);
- });
+
+ it('should not call reaction function on removed group', () {
+ var log = [];
+ context['name'] = 'misko';
+ var child = watchGrp.newGroup(context);
+ watchGrp.watch(parse('name'), (v, _) {
+ log.add('root $v');
+ if (v == 'destroy') {
+ child.remove();
+ }
+ });
+ child.watch(parse('name'), (v, _) => log.add('child $v'));
+ watchGrp.detectChanges();
+ expect(log).toEqual(['root misko', 'child misko']);
+ log.clear();
+
+ context['name'] = 'destroy';
+ watchGrp.detectChanges();
+ expect(log).toEqual(['root destroy']);
+ });
- it('should watch children', () {
- var childContext = new PrototypeMap(context);
- context['a'] = 'OK';
- context['b'] = 'BAD';
- childContext['b'] = 'OK';
- watchGrp.watch(parse('a'), (v, p) => logger(v));
- watchGrp.newGroup(childContext).watch(parse('b'), (v, p) => logger(v));
+ it('should watch children', () {
+ var childContext = new PrototypeMap(context);
+ context['a'] = 'OK';
+ context['b'] = 'BAD';
+ childContext['b'] = 'OK';
+ watchGrp.watch(parse('a'), (v, p) => logger(v));
+ watchGrp.newGroup(childContext).watch(parse('b'), (v, p) => logger(v));
- watchGrp.detectChanges();
- expect(logger).toEqual(['OK', 'OK']);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['OK', 'OK']);
+ logger.clear();
- context['a'] = 'A';
- childContext['b'] = 'B';
+ context['a'] = 'A';
+ childContext['b'] = 'B';
- watchGrp.detectChanges();
- expect(logger).toEqual(['A', 'B']);
- logger.clear();
+ watchGrp.detectChanges();
+ expect(logger).toEqual(['A', 'B']);
+ logger.clear();
+ });
});
- });
-});
+ });
+}
class MyClass {
final Logger logger;

Powered by Google App Engine
This is Rietveld 408576698